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
diff options
context:
space:
mode:
authorVladimirs Maksimovs <vladimirs.maksimovs@zabbix.com>2022-11-04 17:53:45 +0300
committerVladimirs Maksimovs <vladimirs.maksimovs@zabbix.com>2022-11-04 17:53:45 +0300
commit013f74ffb5d098cde3f86e6baba2a135c517373a (patch)
tree2912c10ec62dc429a95a773f77eac493a114ba40
parentb2b8335f76e81892cce2f555caf25b2ffa04432d (diff)
parent4f4d9097321fddf2267c4d9ddd9837ec77491032 (diff)
A.F......T [ZBXNEXT-6470,ZBXNEXT-6980] implemented audit logging of item and item prototype API objects
* commit '4f4d9097321fddf2267c4d9ddd9837ec77491032': (33 commits) .......... [ZBXNEXT-6980] fixed error messages in autotest A......... [ZBXNEXT-6470,ZBXNEXT-6980] prevented logging the non-masked password of HTTP agent item/item prototype into audit log details .......... [ZBXNEXT-6980] removed trailing comma .......... [ZBXNEXT-6980] added several cases to delay validation .......... [ZBXNEXT-6980] removed trailing comma A......... [ZBXNEXT-6470,ZBXNEXT-6980] increased maximum number of inheritable items per iteration .......... [ZBXNEXT-6470,ZBXNEXT-6980] fixed API tests .......... [ZBXNEXT-6980] reverted changes for dependent item test A......... [ZBXNEXT-6980] changed wording of some messages; simplified code; temporarily unlocked dependent item test A......... [ZBXNEXT-6470,ZBXNEXT-6980] removed debugging code A......... [ZBXNEXT-6470,ZBXNEXT-6980] fixed inability to import SNMP trap item or item prototype ....I..... [ZBXNEXT-6470,ZBXNEXT-6980] fixed incorrect itemid specified in trigger prototype expression ....I..... [ZBXNEXT-6470,ZBXNEXT-6980] fixed incorrect LLD rule for item prototype ..F....... [ZBXNEXT-6470,ZBXNEXT-6980] simplified code; updated change-log .........T [ZBXNEXT-6980] fixed duplicate metrics in wildfly and gridgain templates A.F......T [ZBXNEXT-6470,ZBXNEXT-6980] fixed duplicated metrics in WildFly Server by JMX template .........T [ZBXNEXT-6470,ZBXNEXT-6980] removed unrelated fields of item prototypes from template RabbitMQ cluster by Zabbix agent .........T [ZBXNEXT-6470,ZBXNEXT-6980] removed unrelated fields of item prototypes from template RabbitMQ cluster by Zabbix agent .........T [ZBXNEXT-6470,ZBXNEXT-6980] removed unrelated fields of item prototypes from template RabbitMQ cluster by Zabbix agent A......... [ZBXNEXT-6470,ZBXNEXT-6980] fixed incorrect validation of API_REGEX type in case when slash are escaping with backslash in regexp pattern ...
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-69801
-rw-r--r--create/src/templates.tmpl4
-rw-r--r--templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml25
-rw-r--r--templates/app/wildfly_server_jmx/README.md14
-rw-r--r--templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml31
-rw-r--r--templates/db/gridgain_jmx/README.md14
-rw-r--r--templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml14
-rw-r--r--ui/app/controllers/CControllerHostCreate.php3
-rw-r--r--ui/app/controllers/CControllerPopupItemTest.php32
-rw-r--r--ui/app/controllers/CControllerPopupItemTestEdit.php36
-rw-r--r--ui/app/controllers/CControllerPopupItemTestGetValue.php2
-rw-r--r--ui/app/controllers/CControllerPopupItemTestSend.php31
-rw-r--r--ui/app/controllers/CControllerPopupMassupdateItem.php480
-rw-r--r--ui/app/views/js/popup.massupdate.item.js.php74
-rw-r--r--ui/app/views/popup.itemtestedit.view.php2
-rw-r--r--ui/app/views/popup.massupdate.item.php2
-rw-r--r--ui/auditacts.php1
-rw-r--r--ui/disc_prototypes.php387
-rw-r--r--ui/host_discovery.php61
-rw-r--r--ui/include/audit.inc.php43
-rw-r--r--ui/include/classes/api/CAudit.php163
-rw-r--r--ui/include/classes/api/CItemTypeFactory.php97
-rw-r--r--ui/include/classes/api/item_types/CItemType.php470
-rw-r--r--ui/include/classes/api/item_types/CItemTypeCalculated.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeDbMonitor.php80
-rw-r--r--ui/include/classes/api/item_types/CItemTypeDependent.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeExternal.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeHttpAgent.php187
-rw-r--r--ui/include/classes/api/item_types/CItemTypeInternal.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeIpmi.php82
-rw-r--r--ui/include/classes/api/item_types/CItemTypeJmx.php84
-rw-r--r--ui/include/classes/api/item_types/CItemTypeScript.php86
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSimple.php80
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSnmp.php76
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSnmpTrap.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSsh.php116
-rw-r--r--ui/include/classes/api/item_types/CItemTypeTelnet.php84
-rw-r--r--ui/include/classes/api/item_types/CItemTypeTrapper.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeZabbix.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeZabbixActive.php68
-rw-r--r--ui/include/classes/api/managers/CDiscoveryRuleManager.php25
-rw-r--r--ui/include/classes/api/managers/CHttpTestManager.php2183
-rw-r--r--ui/include/classes/api/managers/CItemManager.php262
-rw-r--r--ui/include/classes/api/managers/CItemPrototypeManager.php154
-rw-r--r--ui/include/classes/api/services/CConfiguration.php4
-rw-r--r--ui/include/classes/api/services/CDiscoveryRule.php439
-rw-r--r--ui/include/classes/api/services/CHost.php33
-rw-r--r--ui/include/classes/api/services/CHostGeneral.php419
-rw-r--r--ui/include/classes/api/services/CHostPrototype.php58
-rw-r--r--ui/include/classes/api/services/CHttpTest.php226
-rw-r--r--ui/include/classes/api/services/CItem.php1545
-rw-r--r--ui/include/classes/api/services/CItemGeneral.php4034
-rw-r--r--ui/include/classes/api/services/CItemGeneralOld.php2973
-rw-r--r--ui/include/classes/api/services/CItemPrototype.php1298
-rw-r--r--ui/include/classes/api/services/CTemplate.php31
-rw-r--r--ui/include/classes/core/ZBase.php2
-rw-r--r--ui/include/classes/import/CConfigurationImport.php80
-rw-r--r--ui/include/classes/import/CImportReferencer.php4
-rw-r--r--ui/include/classes/import/converters/C10ImportConverter.php26
-rw-r--r--ui/include/classes/import/converters/C62ImportConverter.php80
-rw-r--r--ui/include/classes/import/validators/C64XmlValidator.php2
-rw-r--r--ui/include/classes/parsers/CPrometheusOutputParser.php9
-rw-r--r--ui/include/classes/parsers/CPrometheusPatternParser.php18
-rw-r--r--ui/include/classes/parsers/CUpdateIntervalParser.php31
-rw-r--r--ui/include/classes/validators/CApiInputValidator.php966
-rw-r--r--ui/include/defines.inc.php11
-rw-r--r--ui/include/items.inc.php909
-rw-r--r--ui/include/views/configuration.item.edit.php2
-rw-r--r--ui/include/views/configuration.item.prototype.edit.php8
-rw-r--r--ui/items.php526
-rw-r--r--ui/js/pages/items.js4
-rw-r--r--ui/templates.php2
-rw-r--r--ui/tests/api_json/ApiJsonTests.php4
-rw-r--r--ui/tests/api_json/data/data_test.sql18
-rwxr-xr-xui/tests/api_json/testAuditlogScheduledReport.php2
-rw-r--r--ui/tests/api_json/testDependentItems.php435
-rw-r--r--ui/tests/api_json/testIconMap.php20
-rw-r--r--ui/tests/api_json/testItem.php113
-rw-r--r--ui/tests/api_json/testItemPrototype.php115
-rw-r--r--ui/tests/api_json/testWebScenario.php6
-rw-r--r--ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml14
-rw-r--r--ui/tests/include/helpers/CDataHelper.php5
-rw-r--r--ui/tests/integration/data/confsync_hosts.xml5
-rw-r--r--ui/tests/integration/data/confsync_hosts_updated.xml113
-rw-r--r--ui/tests/integration/data/confsync_tmpl.xml13
-rw-r--r--ui/tests/integration/data/confsync_tmpl_updated.xml87
-rw-r--r--ui/tests/integration/testActiveAvailability.php2
-rw-r--r--ui/tests/integration/testGoAgentDataCollection.php16
-rw-r--r--ui/tests/integration/testInitialConfSync.php6
-rw-r--r--ui/tests/integration/testItemState.php10
-rw-r--r--ui/tests/selenium/common/testCalculatedFormula.php2
-rw-r--r--ui/tests/selenium/common/testFormPreprocessing.php520
-rw-r--r--ui/tests/selenium/common/testFormTags.php4
-rw-r--r--ui/tests/selenium/common/testItemTest.php34
-rw-r--r--ui/tests/selenium/common/testMassUpdateItems.php141
-rw-r--r--ui/tests/selenium/data/data_test.sql174
-rw-r--r--ui/tests/selenium/items/testFormItem.php41
-rw-r--r--ui/tests/selenium/items/testFormItemHttpAgent.php50
-rw-r--r--ui/tests/selenium/items/testFormItemPrototype.php323
-rw-r--r--ui/tests/selenium/items/testFormTestItem.php2
-rw-r--r--ui/tests/selenium/items/testFormTestItemPrototype.php8
-rw-r--r--ui/tests/selenium/items/testInheritanceItem.php3
-rw-r--r--ui/tests/selenium/items/testInheritanceItemPrototype.php8
-rw-r--r--ui/tests/selenium/items/testItemTypeSelection.php18
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingItem.php6
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php8
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php3
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingTest.php50
-rw-r--r--ui/tests/selenium/testFormTriggerPrototype.php30
-rw-r--r--ui/tests/selenium/testInheritanceTriggerPrototype.php2
-rw-r--r--ui/tests/selenium/testPageMassUpdateItemPrototypes.php27
-rw-r--r--ui/tests/selenium/testPageMassUpdateItems.php27
-rw-r--r--ui/tests/selenium/testTemplateInheritance.php12
-rw-r--r--ui/tests/unit/include/classes/import/CImportDataAdapterTest.php12
-rw-r--r--ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php42
-rw-r--r--ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php169
-rw-r--r--ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php2604
117 files changed, 17440 insertions, 7646 deletions
diff --git a/ChangeLog.d/feature/ZBXNEXT-6980 b/ChangeLog.d/feature/ZBXNEXT-6980
new file mode 100644
index 00000000000..5ec63a688a5
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-6980
@@ -0,0 +1 @@
+A.F......T [ZBXNEXT-6470,ZBXNEXT-6980] implemented audit logging of item and item prototype API objects (abiba, agriscenko, dgoloscapov, jfreibergs, vmaksimovs)
diff --git a/create/src/templates.tmpl b/create/src/templates.tmpl
index 9f357169de3..06b37c28966 100644
--- a/create/src/templates.tmpl
+++ b/create/src/templates.tmpl
@@ -4451,7 +4451,7 @@ ROW |34977 |16 |
ROW |34978 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache delete, rate |jmx["{#JMXOBJ}",PreparedStatementCacheDeleteCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of statements discarded from the cache per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |9c08f05299774a538a52be2c11e62cda|
ROW |34979 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache hit, rate |jmx["{#JMXOBJ}",PreparedStatementCacheHitCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of times that statements from the cache were used per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |dbe7bc3d9a8a46c6a84b956f0a9e33be|
ROW |34980 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache miss, rate |jmx["{#JMXOBJ}",PreparedStatementCacheMissCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of times that a statement request could not be satisfied with a statement from the cache per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |0e81a30b802941c4bc0e34a10a8d0573|
-ROW |34981 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |jmx["{#JMXOBJ}",statisticsEnabled] |1m |7d |365d |0 |3 | | | | |NULL |335 | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |Define whether runtime statistics are enabled or not. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |81dbce02ad4d492ebebd1a205bea1b43|
+ROW |34981 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"] |1m |7d |365d |0 |3 | | | | |NULL |335 | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |Define whether runtime statistics are enabled or not. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |81dbce02ad4d492ebebd1a205bea1b43|
ROW |34982 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Connections: Active |jmx["{#JMXOBJ}",ActiveCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of open connections. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |06d61e390b0e403e81b1eaa77815cde2|
ROW |34983 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: XA: Commit time, avg |jmx["{#JMXOBJ}",XACommitAverageTime] |1m |7d |365d |0 |0 | |s | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The average time for a XAResource commit invocation. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |c22389aa983a4cdca504e3cc41126f03|
ROW |34984 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: XA: Start time, avg |jmx["{#JMXOBJ}",XAStartAverageTime] |1m |7d |365d |0 |0 | |s | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The average time for a XAResource start invocation. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |ab9c32db31d84a46967c1f601f11f610|
@@ -24089,7 +24089,7 @@ ROW |23122 |34960 |18918 |find |$,,"like","running"
ROW |23126 |35001 |18920 |min |$,5m |
ROW |23127 |34997 |18920 |last |$ |
ROW |23128 |34982 |18921 |max |$,5m |
-ROW |23129 |34999 |18922 |last |$ |
+ROW |23129 |34981 |18922 |last |$ |
ROW |23130 |34999 |18923 |last |$ |
ROW |23131 |34998 |18924 |last |$ |
ROW |23132 |34996 |18925 |min |$,5m |
diff --git a/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml b/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
index bff4740c842..dedb1a44a40 100644
--- a/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
+++ b/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
@@ -1099,8 +1099,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: alarms in effect in the cluster{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.CLUSTER_HOST}:{$RABBITMQ.API.PORT}/api/health/checks/alarms{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if there are no alarms in effect in the cluster, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -1122,9 +1120,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- url: '{$RABBITMQ.API.SCHEME}://{HOST.CONN}:{$RABBITMQ.API.PORT}/api/health/checks/alarms'
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -1992,8 +1987,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: expiration date on the certificates{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/certificate-expiration/1/months{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks the expiration date on the certificates for every listener configured to use TLS. Responds a 200 OK if all certificates are valid (have not expired), otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2015,8 +2008,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2041,8 +2032,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: local alarms in effect on this node{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/local-alarms{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if there are no local alarms in effect on the target node, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2064,8 +2053,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2090,8 +2077,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: classic mirrored queues without synchronized mirrors online{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/node-is-mirror-sync-critical{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks if there are classic mirrored queues without synchronized mirrors online (queues that would potentially lose data if the target node is shut down). Responds a 200 OK if there are no such classic mirrored queues, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2113,8 +2098,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2139,8 +2122,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: queues with minimum online quorum{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/node-is-quorum-critical{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks if there are quorum queues with minimum online quorum (queues that would lose their quorum and availability if the target node is shut down). Responds a 200 OK if there are no such quorum queues, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2162,8 +2143,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2188,8 +2167,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: virtual hosts on this node{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/virtual-hosts{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if all virtual hosts and running on the target node, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2211,8 +2188,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
diff --git a/templates/app/wildfly_server_jmx/README.md b/templates/app/wildfly_server_jmx/README.md
index 05f3170bd91..0202fc30173 100644
--- a/templates/app/wildfly_server_jmx/README.md
+++ b/templates/app/wildfly_server_jmx/README.md
@@ -3,7 +3,7 @@
## Overview
-For Zabbix version: 6.2 and higher
+For Zabbix version: 6.2 and higher.
Official JMX Template for WildFly server.
@@ -86,7 +86,7 @@ There are no template links in this template.
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache delete, rate |<p>The number of statements discarded from the cache per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheDeleteCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache hit, rate |<p>The number of times that statements from the cache were used per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheHitCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache miss, rate |<p>The number of times that a statement request could not be satisfied with a statement from the cache per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheMissCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
-|WildFly |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |<p>Define whether runtime statistics are enabled or not.</p> |JMX |jmx["{#JMXOBJ}",statisticsEnabled]<p>**Preprocessing**:</p><p>- BOOL_TO_DECIMAL</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `3h`</p> |
+|WildFly |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |<p>Define whether runtime statistics are enabled or not.</p> |JMX |jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"]<p>**Preprocessing**:</p><p>- BOOL_TO_DECIMAL</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `3h`</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Connections: Active |<p>The number of open connections.</p> |JMX |jmx["{#JMXOBJ}",ActiveCount] |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Connections: Available |<p>The available count.</p> |JMX |jmx["{#JMXOBJ}",AvailableCount] |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Blocking time, avg |<p>Average Blocking Time for pool.</p> |JMX |jmx["{#JMXOBJ}",AverageBlockingTime] |
@@ -128,11 +128,11 @@ There are no template links in this template.
|----|-----------|----|----|----|
|WildFly: Server needs to restart for configuration change. |<p>-</p> |`find(/WildFly Server by JMX/jmx["jboss.as:management-root=server","runtimeConfigurationState"],,"like","ok")=0` |WARNING | |
|WildFly: Server controller is not in RUNNING state |<p>-</p> |`find(/WildFly Server by JMX/jmx["jboss.as:management-root=server","serverState"],,"like","running")=0` |WARNING |<p>**Depends on**:</p><p>- WildFly: Server needs to restart for configuration change.</p> |
-|WildFly: Version has changed |<p>WildFly version has changed. Ack to close.</p> |`last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#1)<>last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#2) and length(last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"]))>0` |INFO |<p>Manual close: YES</p> |
-|WildFly: has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m` |INFO |<p>Manual close: YES</p> |
+|WildFly: Version has changed |<p>The WildFly version has changed. Perform Ack to close.</p> |`last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#1)<>last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#2) and length(last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"]))>0` |INFO |<p>Manual close: YES</p> |
+|WildFly: Host has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m` |INFO |<p>Manual close: YES</p> |
|WildFly: Failed to fetch info data |<p>Zabbix has not received data for items for the last 15 minutes</p> |`nodata(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"],15m)=1` |WARNING | |
|WildFly deployment [{#DEPLOYMENT}]: Deployment status has changed |<p>Deployment status has changed. Ack to close.</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status],#1)<>last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status],#2) and length(last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status]))>0` |WARNING |<p>Manual close: YES</p> |
-|WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled |<p>-</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0` |INFO | |
+|WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled |<p>-</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"])=0` |INFO | |
|WildFly {#JMX_DATA_SOURCE}: There are no active connections for 5m |<p>-</p> |`max(/WildFly Server by JMX/jmx["{#JMXOBJ}",ActiveCount],5m)=0` |WARNING | |
|WildFly {#JMX_DATA_SOURCE}: Connection usage is too high |<p>-</p> |`min(/WildFly Server by JMX/jmx["{#JMXOBJ}",InUseCount],5m)/last(/WildFly Server by JMX/jmx["{#JMXOBJ}",AvailableCount])*100>{$WILDFLY.CONN.USAGE.WARN.MAX}` |HIGH | |
|WildFly {#JMX_DATA_SOURCE}: Pools monitoring statistic is not enabled |<p>Zabbix has not received data for items for the last 15 minutes</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0` |INFO | |
@@ -142,7 +142,7 @@ There are no template links in this template.
## Feedback
-Please report any issues with the template at https://support.zabbix.com
+Please report any issues with the template at https://support.zabbix.com.
-You can also provide feedback, discuss the template or ask for help with it at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback).
+You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback).
diff --git a/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml b/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
index d914f2db55e..41600d586e8 100644
--- a/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
+++ b/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
@@ -1,6 +1,6 @@
zabbix_export:
version: '6.2'
- date: '2022-06-07T19:37:55Z'
+ date: '2022-11-01T16:07:09Z'
template_groups:
-
uuid: a571c0d144b14fd4a87a9d9b2aa9fcd6
@@ -18,7 +18,7 @@ zabbix_export:
You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback
- Template tooling version used: 0.41
+ Template tooling version used: 0.42
groups:
-
name: Templates/Applications
@@ -58,8 +58,8 @@ zabbix_export:
-
uuid: 71db189440ec4355aa06262e3200d47e
expression: 'last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m'
- name: 'WildFly: has been restarted'
- event_name: 'WildFly: has been restarted (uptime < 10m)'
+ name: 'WildFly: Host has been restarted'
+ event_name: 'WildFly: {HOST.NAME} has been restarted (uptime < 10m)'
priority: INFO
description: 'Uptime is less than 10 minutes.'
manual_close: 'YES'
@@ -160,7 +160,7 @@ zabbix_export:
name: 'WildFly: Version has changed'
event_name: 'WildFly: Version has changed (new version: {ITEM.VALUE})'
priority: INFO
- description: 'WildFly version has changed. Ack to close.'
+ description: 'The WildFly version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
@@ -772,7 +772,7 @@ zabbix_export:
uuid: 81dbce02ad4d492ebebd1a205bea1b43
name: 'WildFly {#JMX_DATA_SOURCE}: Statistics enabled'
type: JMX
- key: 'jmx["{#JMXOBJ}",statisticsEnabled]'
+ key: 'jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"]'
history: 7d
username: '{$WILDFLY.USER}'
password: '{$WILDFLY.PASSWORD}'
@@ -796,6 +796,16 @@ zabbix_export:
-
tag: datasource
value: '{#JMX_DATA_SOURCE}'
+ trigger_prototypes:
+ -
+ uuid: 2996a48892a640d69154de332c673755
+ expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"])=0'
+ name: 'WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled'
+ priority: INFO
+ tags:
+ -
+ tag: scope
+ value: notice
graph_prototypes:
-
uuid: 322b26787ab444db90a1be3da2717dd2
@@ -1178,15 +1188,6 @@ zabbix_export:
value: '{#JMX_DATA_SOURCE}'
trigger_prototypes:
-
- uuid: 2996a48892a640d69154de332c673755
- expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0'
- name: 'WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled'
- priority: INFO
- tags:
- -
- tag: scope
- value: notice
- -
uuid: 4ea71ba3572647b29a60077bc511052a
expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0'
name: 'WildFly {#JMX_DATA_SOURCE}: Pools monitoring statistic is not enabled'
diff --git a/templates/db/gridgain_jmx/README.md b/templates/db/gridgain_jmx/README.md
index 59b46efcac4..014f9c733d4 100644
--- a/templates/db/gridgain_jmx/README.md
+++ b/templates/db/gridgain_jmx/README.md
@@ -3,7 +3,7 @@
## Overview
-For Zabbix version: 6.2 and higher
+For Zabbix version: 6.2 and higher.
Official JMX Template for GridGain In-Memory Computing Platform.
This template is based on the original template developed by Igor Akkuratov, Senior Engineer at GridGain Systems and GridGain In-Memory Computing Platform Contributor.
@@ -99,7 +99,7 @@ There are no template links in this template.
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication outbound messages queue |<p>Outbound messages queue size.</p> |JMX |jmx["{#JMXOBJ}",OutboundMessagesQueueSize] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication messages received, rate |<p>The number of messages received per second.</p> |JMX |jmx["{#JMXOBJ}",ReceivedMessagesCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication messages sent, rate |<p>The number of messages sent per second.</p> |JMX |jmx["{#JMXOBJ}",SentMessagesCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
-|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate |<p>Gets maximum number of reconnect attempts used when establishing connection with remote nodes per second.</p> |JMX |jmx["{#JMXOBJ}",ReconnectCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
+|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate |<p>Gets maximum number of reconnect attempts used when establishing connection with remote nodes per second.</p> |JMX |jmx["{#JMXOBJ}",ReconnectCount, "maxNumbers"]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Locked keys |<p>The number of keys locked on the node.</p> |JMX |jmx["{#JMXOBJ}",LockedKeysNumber] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Transactions owner, current |<p>The number of active transactions for which this node is the initiator.</p> |JMX |jmx["{#JMXOBJ}",OwnerTransactionsNumber] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Transactions holding lock, current |<p>The number of active transactions holding at least one key lock.</p> |JMX |jmx["{#JMXOBJ}",TransactionsHoldingLockNumber] |
@@ -143,9 +143,9 @@ There are no template links in this template.
|Name|Description|Expression|Severity|Dependencies and additional info|
|----|-----------|----|----|----|
-|GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m` |INFO |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Host has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Failed to fetch info data |<p>Zabbix has not received data for items for the last 10 minutes.</p> |`nodata(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime],10m)=1` |WARNING |<p>Manual close: YES</p> |
-|GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed |<p>GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion]))>0` |INFO |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed |<p>The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion]))>0` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Server node left the topology |<p>One or more server node left the topology. Ack to close.</p> |`change(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])<0` |WARNING |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Server node added to the topology |<p>One or more server node added to the topology. Ack to close.</p> |`change(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])>0` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: There are nodes is not in topology |<p>One or more server node left the topology. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])>last(/GridGain by JMX/jmx["{#JMXOBJ}",TotalBaselineNodes])` |INFO |<p>Manual close: YES</p> |
@@ -153,7 +153,7 @@ There are no template links in this template.
|GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long |<p>PME duration is over {$GRIDGAIN.PME.DURATION.MAX.WARN}ms.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentPmeDuration],5m) > {$GRIDGAIN.PME.DURATION.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long |<p>PME duration is over {$GRIDGAIN.PME.DURATION.MAX.HIGH}ms. Looks like PME is hung.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentPmeDuration],5m) > {$GRIDGAIN.PME.DURATION.MAX.HIGH}` |HIGH | |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Number of running threads is too high |<p>Number of running threads is over {$GRIDGAIN.THREADS.COUNT.MAX.WARN}.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentThreadCount],15m) > {$GRIDGAIN.THREADS.COUNT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long</p> |
-|GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed |<p>GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator]))>0` |WARNING |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed |<p>The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator]))>0` |WARNING |<p>Manual close: YES</p> |
|Cache group [{#JMXGROUP}]: There are no success transactions for cache for 5m |<p>-</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxRollbacks],5m)>0 and max(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxCommits],5m)=0` |AVERAGE | |
|Cache group [{#JMXGROUP}]: Success transactions less than rollbacks for 5m |<p>-</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxRollbacks],5m) > max(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxCommits],5m)` |WARNING |<p>**Depends on**:</p><p>- Cache group [{#JMXGROUP}]: There are no success transactions for cache for 5m</p> |
|Cache group [{#JMXGROUP}]: All entries are in heap |<p>All entries are in heap. Possibly you use eager queries it may cause out of memory exceptions for big caches. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",CacheSize])=last(/GridGain by JMX/jmx["{#JMXOBJ}",HeapEntriesCount])` |INFO |<p>Manual close: YES</p> |
@@ -171,7 +171,7 @@ There are no template links in this template.
## Feedback
-Please report any issues with the template at https://support.zabbix.com
+Please report any issues with the template at https://support.zabbix.com.
-You can also provide feedback, discuss the template or ask for help with it at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/).
+You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/).
diff --git a/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml b/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
index 3cb59358413..9262f10dd72 100644
--- a/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
+++ b/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
@@ -1,6 +1,6 @@
zabbix_export:
version: '6.2'
- date: '2022-06-07T19:33:22Z'
+ date: '2022-11-01T16:08:32Z'
template_groups:
-
uuid: 748ad4d098d447d492bb935c907f652f
@@ -19,7 +19,7 @@ zabbix_export:
You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/
- Template tooling version used: 0.41
+ Template tooling version used: 0.42
groups:
-
name: Templates/Databases
@@ -865,7 +865,7 @@ zabbix_export:
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed'
event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed (new version: {ITEM.VALUE})'
priority: INFO
- description: 'GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.'
+ description: 'The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
@@ -928,8 +928,8 @@ zabbix_export:
-
uuid: 23cd9dbb498f4bb095ec8be1693fccf0
expression: 'last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m'
- name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted'
- event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted (uptime < 10m)'
+ name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Host has been restarted'
+ event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: {HOST.NAME} has been restarted (uptime < 10m)'
priority: INFO
description: 'Uptime is less than 10 minutes.'
manual_close: 'YES'
@@ -1008,7 +1008,7 @@ zabbix_export:
uuid: 613114b7abb94bd5bbabc2d1d19975b7
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate'
type: JMX
- key: 'jmx["{#JMXOBJ}",ReconnectCount]'
+ key: 'jmx["{#JMXOBJ}",ReconnectCount, "maxNumbers"]'
history: 7d
value_type: FLOAT
username: '{$GRIDGAIN.USER}'
@@ -1118,7 +1118,7 @@ zabbix_export:
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed'
event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed (new version: {ITEM.VALUE})'
priority: WARNING
- description: 'GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.'
+ description: 'The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
diff --git a/ui/app/controllers/CControllerHostCreate.php b/ui/app/controllers/CControllerHostCreate.php
index c7f835922d0..ad46aeeb59b 100644
--- a/ui/app/controllers/CControllerHostCreate.php
+++ b/ui/app/controllers/CControllerHostCreate.php
@@ -117,6 +117,7 @@ class CControllerHostCreate extends CControllerHostUpdateGeneral {
$result = DBend(true);
}
catch (Exception $e) {
+ $result = false;
DBend(false);
}
@@ -205,7 +206,7 @@ class CControllerHostCreate extends CControllerHostUpdateGeneral {
return false;
}
- if (!copyItems($src_hostid, $hostid, true)) {
+ if (!copyItemsToHosts('hostids', [$src_hostid], false, [$hostid])) {
return false;
}
diff --git a/ui/app/controllers/CControllerPopupItemTest.php b/ui/app/controllers/CControllerPopupItemTest.php
index 5332fcfb213..bc50bbbab06 100644
--- a/ui/app/controllers/CControllerPopupItemTest.php
+++ b/ui/app/controllers/CControllerPopupItemTest.php
@@ -247,9 +247,9 @@ abstract class CControllerPopupItemTest extends CController {
protected $is_item_testable;
/**
- * @var object
+ * @var string
*/
- protected $preproc_item;
+ protected $test_type;
/**
* @var array
@@ -332,26 +332,6 @@ abstract class CControllerPopupItemTest extends CController {
}
/**
- * Function returns instance of item, item prototype or discovery rule class.
- *
- * @param int $test_type
- *
- * @return CItem|CItemPrototype|CDiscoveryRule
- */
- protected static function getPreprocessingItemClassInstance($test_type) {
- switch ($test_type) {
- case self::ZBX_TEST_TYPE_ITEM:
- return new CItem;
-
- case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
- return new CItemPrototype;
-
- case self::ZBX_TEST_TYPE_LLD:
- return new CDiscoveryRule;
- }
- }
-
- /**
* Function returns list of proxies.
*
* @return array
@@ -929,7 +909,7 @@ abstract class CControllerPopupItemTest extends CController {
'supported_macros' => array_diff_key($this->macros_by_item_props['key'],
['support_user_macros' => true, 'support_lld_macros' => true]
),
- 'support_lldmacros' => ($this->preproc_item instanceof CItemPrototype),
+ 'support_lldmacros' => ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
'texts_support_macros' => [$inputs['key']],
'texts_support_lld_macros' => [$inputs['key']],
'texts_support_user_macros' => [$inputs['key']],
@@ -1025,7 +1005,7 @@ abstract class CControllerPopupItemTest extends CController {
protected function resolvePreprocessingStepMacros(array $steps) {
// Resolve macros used in parameter fields.
$macros_posted = $this->getInput('macros', []);
- $macros_types = ($this->preproc_item instanceof CItemPrototype)
+ $macros_types = ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE)
? ['usermacros' => true, 'lldmacros' => true]
: ['usermacros' => true];
@@ -1069,7 +1049,7 @@ abstract class CControllerPopupItemTest extends CController {
$expression_parser = new CExpressionParser([
'usermacros' => true,
- 'lldmacros' => ($this->preproc_item instanceof CItemPrototype),
+ 'lldmacros' => ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
'calculated' => true,
'host_macro' => true,
'empty_host' => true
@@ -1200,7 +1180,7 @@ abstract class CControllerPopupItemTest extends CController {
'macros_n' => []
];
- if ($this->preproc_item instanceof CItemPrototype) {
+ if ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE) {
$types += ['lldmacros' => true];
}
diff --git a/ui/app/controllers/CControllerPopupItemTestEdit.php b/ui/app/controllers/CControllerPopupItemTestEdit.php
index f94862af32e..bae0c4df2ed 100644
--- a/ui/app/controllers/CControllerPopupItemTestEdit.php
+++ b/ui/app/controllers/CControllerPopupItemTestEdit.php
@@ -85,7 +85,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
if ($ret) {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1;
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
// Check if key is valid for item types it's mandatory.
@@ -105,10 +105,31 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
$steps = $this->getInput('steps', []);
if ($ret && $steps) {
$steps = normalizeItemPreprocessingSteps($steps);
- $steps_validation_response = $this->preproc_item->validateItemPreprocessingSteps($steps);
- if ($steps_validation_response !== true) {
- error($steps_validation_response);
- $ret = false;
+
+ if ($this->test_type == self::ZBX_TEST_TYPE_LLD) {
+ $lld_instance = new CDiscoveryRule();
+ $steps_validation_response = $lld_instance->validateItemPreprocessingSteps($steps);
+
+ if ($steps_validation_response !== true) {
+ error($steps_validation_response);
+ $ret = false;
+ }
+ }
+ else {
+ switch ($this->test_type) {
+ case self::ZBX_TEST_TYPE_ITEM:
+ $api_input_rules = CItem::getPreprocessingValidationRules();
+ break;
+
+ case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
+ $api_input_rules = CItemPrototype::getPreprocessingValidationRules();
+ break;
+ }
+
+ if (!CApiInputValidator::validate($api_input_rules, $steps, '/', $error)) {
+ error($error);
+ $ret = false;
+ }
}
}
elseif ($ret && !$this->is_item_testable) {
@@ -157,7 +178,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
$preprocessing_types = zbx_objectValues($preprocessing_steps, 'type');
$preprocessing_names = get_preprocessing_types(null, false, $preprocessing_types);
- $support_lldmacros = ($this->preproc_item instanceof CItemPrototype);
+ $support_lldmacros = ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE);
$show_prev = (count(array_intersect($preprocessing_types, self::$preproc_steps_using_prev_value)) > 0);
// Collect item texts and macros to later check their usage.
@@ -392,7 +413,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
'prev_time' => $prev_time,
'hostid' => $this->getInput('hostid'),
'interfaceid' => $this->getInput('interfaceid', 0),
- 'test_type' => $this->getInput('test_type'),
+ 'test_type' => $this->test_type,
'step_obj' => $this->getInput('step_obj'),
'show_final_result' => $this->getInput('show_final_result'),
'valuemapid' => $this->getInput('valuemapid', 0),
@@ -409,7 +430,6 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
'interface_port_enabled' => (array_key_exists($this->item_type, $this->items_require_interface)
&& $this->items_require_interface[$this->item_type]['port']
),
- 'preproc_item' => $this->preproc_item,
'show_snmp_form' => ($this->item_type == ITEM_TYPE_SNMP),
'show_warning' => $show_warning,
'user' => [
diff --git a/ui/app/controllers/CControllerPopupItemTestGetValue.php b/ui/app/controllers/CControllerPopupItemTestGetValue.php
index 42ea46074c9..a8b3f9301ad 100644
--- a/ui/app/controllers/CControllerPopupItemTestGetValue.php
+++ b/ui/app/controllers/CControllerPopupItemTestGetValue.php
@@ -77,7 +77,7 @@ class CControllerPopupItemTestGetValue extends CControllerPopupItemTest {
if ($ret) {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->item_type = $this->getInput('item_type');
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
if (!$this->is_item_testable) {
diff --git a/ui/app/controllers/CControllerPopupItemTestSend.php b/ui/app/controllers/CControllerPopupItemTestSend.php
index 82bc61d32c4..1a9b9d16111 100644
--- a/ui/app/controllers/CControllerPopupItemTestSend.php
+++ b/ui/app/controllers/CControllerPopupItemTestSend.php
@@ -114,7 +114,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->get_value_from_host = (bool) $this->getInput('get_value');
$this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1;
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
$interface = $this->getInput('interface', []);
@@ -158,9 +158,32 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
}
// Check preprocessing steps.
- if ($steps && ($error = $this->preproc_item->validateItemPreprocessingSteps($steps)) !== true) {
- error($error);
- $ret = false;
+ if ($steps) {
+ if ($this->test_type == self::ZBX_TEST_TYPE_LLD) {
+ $lld_instance = new CDiscoveryRule();
+ $steps_validation_response = $lld_instance->validateItemPreprocessingSteps($steps);
+
+ if ($steps_validation_response !== true) {
+ error($steps_validation_response);
+ $ret = false;
+ }
+ }
+ else {
+ switch ($this->test_type) {
+ case self::ZBX_TEST_TYPE_ITEM:
+ $api_input_rules = CItem::getPreprocessingValidationRules();
+ break;
+
+ case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
+ $api_input_rules = CItemPrototype::getPreprocessingValidationRules();
+ break;
+ }
+
+ if (!CApiInputValidator::validate($api_input_rules, $steps, '/', $error)) {
+ error($error);
+ $ret = false;
+ }
+ }
}
// Check previous time.
diff --git a/ui/app/controllers/CControllerPopupMassupdateItem.php b/ui/app/controllers/CControllerPopupMassupdateItem.php
index 0f6aad4c194..e983b4ea7a9 100644
--- a/ui/app/controllers/CControllerPopupMassupdateItem.php
+++ b/ui/app/controllers/CControllerPopupMassupdateItem.php
@@ -23,86 +23,61 @@ require_once dirname(__FILE__).'/../../include/forms.inc.php';
class CControllerPopupMassupdateItem extends CController {
- private $opt_interfaceid_expected = false;
-
protected function checkInput() {
$fields = [
- 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]),
- 'authtype' => 'string',
'context' => 'required|string|in host,template',
- 'delay' => 'string',
- 'delay_flex' => 'array',
- 'description' => 'string',
- 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER,
- 'headers' => 'array',
- 'history' => 'string',
- 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
'ids' => 'required|array_id',
- 'interfaceid' => 'id',
- 'jmx_endpoint' => 'string',
- 'logtimefmt' => 'string',
- 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
- 'master_itemid' => 'id',
- 'parent_discoveryid' => 'id',
- 'password' => 'string',
- 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
- 'posts' => 'string',
- 'preprocessing' => 'array',
- 'privatekey' => 'string',
'prototype' => 'required|in 0,1',
- 'publickey' => 'string',
- 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]),
- 'tags' => 'array',
- 'trapper_hosts' => 'string',
- 'trends' => 'string',
+ 'update' => 'in 1',
+ 'visible' => 'array',
+ 'parent_discoveryid' => 'id',
+ 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
'trends_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
- 'timeout' => 'string',
+ 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
+ 'delay_flex' => 'array',
+
+ // The fields used for all item types.
'type' => 'int32',
- 'units' => 'string',
- 'update' => 'in 1',
- 'url' => 'string',
- 'username' => 'string',
'value_type' => 'in '.implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT]),
+ 'units' => 'string',
+ 'history' => 'string',
+ 'trends' => 'string',
'valuemapid' => 'id',
- 'visible' => 'array'
- ];
+ 'logtimefmt' => 'string',
+ 'description' => 'string',
+ 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]),
+ 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER,
+ 'tags' => 'array',
+ 'preprocessing' => 'array',
- $this->opt_interfaceid_expected = (getRequest('interfaceid') == INTERFACE_TYPE_OPT);
+ // The fields used for multiple item types.
+ 'interfaceid' => 'id',
+ 'authtype' => 'string',
+ 'username' => 'string',
+ 'password' => 'string',
+ 'timeout' => 'string',
+ 'delay' => 'string',
+ 'trapper_hosts' => 'string',
- if ($this->opt_interfaceid_expected) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
- }
+ // Dependent item type specific fields.
+ 'master_itemid' => 'id',
- $ret = $this->validateInput($fields);
+ // HTTP Agent item type specific fields.
+ 'url' => 'string',
+ 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
+ 'posts' => 'string',
+ 'headers' => 'array',
+ 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]),
- if ($ret && $this->opt_interfaceid_expected) {
- if ($this->hasInput('type')) {
- $item_types = [$this->getInput('type')];
- }
- else {
- $options = [
- 'output' => ['type'],
- 'itemids' => $this->getInput('ids')
- ];
- $item_types = (bool) $this->getInput('prototype')
- ? API::ItemPrototype()->get($options)
- : API::Item()->get($options);
-
- $item_types = array_column($item_types, 'type', 'type');
- }
+ // JMX item type specific fields.
+ 'jmx_endpoint' => 'string',
- foreach ($item_types as $item_type) {
- if (itemTypeInterface($item_type) != INTERFACE_TYPE_OPT) {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Host interface'),
- interfaceType2str(INTERFACE_TYPE_OPT)
- ));
- $ret = false;
+ // SSH item type specific fields.
+ 'publickey' => 'string',
+ 'privatekey' => 'string'
+ ];
- break;
- }
- }
- }
+ $ret = $this->validateInput($fields);
if (!$ret) {
$this->setResponse(
@@ -118,52 +93,26 @@ class CControllerPopupMassupdateItem extends CController {
}
protected function checkPermissions() {
- $entity = ($this->getInput('prototype') == 1) ? API::ItemPrototype() : API::Item();
-
- return (bool) $entity->get([
- 'output' => [],
- 'itemids' => $this->getInput('ids'),
- 'editable' => true,
- 'limit' => 1
- ]);
- }
-
- protected function doAction() {
- $this->setResponse($this->hasInput('update') ? $this->update() : $this->form());
- }
-
- /**
- * Get array of updated items or item prototypes.
- *
- * @return array
- */
- protected function getItemsOrPrototypes(): array {
- $options = [
- 'output' => ['itemid', 'type'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => $this->getInput('ids'),
- 'preservekeys' => true
- ];
-
- if ($this->getInput('prototype')) {
- $result = API::ItemPrototype()->get($options);
+ if ($this->getInput('prototype') == 1) {
+ $count = API::ItemPrototype()->get([
+ 'countOutput' => true,
+ 'itemids' => $this->getInput('ids'),
+ 'editable' => true
+ ]);
}
else {
- $options['output'][] = 'flags';
- $result = API::Item()->get($options);
+ $count = API::Item()->get([
+ 'countOutput' => true,
+ 'itemids' => $this->getInput('ids'),
+ 'editable' => true
+ ]);
}
- return $result;
+ return $count != 0;
}
- /**
- * Update item or item prototype, return update action status.
- *
- * @param array $data Array of item or item prototypes data to update.
- * @return bool
- */
- protected function updateItemOrPrototype(array $data): bool {
- return (bool) ($this->getInput('prototype') ? API::ItemPrototype()->update($data) : API::Item()->update($data));
+ protected function doAction() {
+ $this->setResponse($this->hasInput('update') ? $this->update() : $this->form());
}
/**
@@ -172,189 +121,159 @@ class CControllerPopupMassupdateItem extends CController {
* @return CControllerResponse
*/
protected function update(): CControllerResponse {
- $result = true;
- $ids = $this->getInput('ids');
- $prototype = (bool) $this->getInput('prototype');
- $input = [
- 'allow_traps' => HTTPCHECK_ALLOW_TRAPS_OFF,
- 'authtype' => '',
- 'delay' => DB::getDefault('items', 'delay'),
- 'description' => '',
- 'discover' => ZBX_PROTOTYPE_DISCOVER,
- 'headers' => [],
- 'history' => ITEM_NO_STORAGE_VALUE,
- 'jmx_endpoint' => '',
- 'logtimefmt' => '',
- 'master_itemid' => 0,
- 'password' => '',
- 'post_type' => ZBX_POSTTYPE_RAW,
- 'posts' => '',
- 'preprocessing' => [],
- 'privatekey' => '',
- 'publickey' => '',
- 'status' => ITEM_STATUS_ACTIVE,
- 'tags' => [],
- 'timeout' => '',
- 'trapper_hosts' => '',
- 'trends' => ITEM_NO_STORAGE_VALUE,
- 'type' => 0,
- 'units' => '',
- 'url' => '',
- 'username' => '',
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'valuemapid' => 0,
- 'interfaceid' => $this->opt_interfaceid_expected ? 0 : ''
- ];
- $this->getInputs($input, array_keys($input));
-
- if ($this->getInput('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) {
- $input['trends'] = ITEM_NO_STORAGE_VALUE;
- }
+ $item_prototypes = (bool) $this->getInput('prototype', false);
- if ($this->getInput('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) {
- $input['history'] = ITEM_NO_STORAGE_VALUE;
- }
+ try {
+ $input = [
+ 'type' => DB::getDefault('items', 'type'),
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => DB::getDefault('items', 'units'),
+ 'history' => ITEM_NO_STORAGE_VALUE,
+ 'trends' => ITEM_NO_STORAGE_VALUE,
+ 'valuemapid' => 0,
+ 'logtimefmt' => DB::getDefault('items', 'logtimefmt'),
+ 'description' => DB::getDefault('items', 'description'),
+ 'status' => DB::getDefault('items', 'status'),
+ 'discover' => DB::getDefault('items', 'discover'),
+ 'tags' => [],
+ 'preprocessing' => [],
+
+ // The fields used for multiple item types.
+ 'interfaceid' => 0,
+ 'authtype' => DB::getDefault('items', 'authtype'),
+ 'username' => DB::getDefault('items', 'username'),
+ 'password' => DB::getDefault('items', 'password'),
+ 'timeout' => '',
+ 'delay' => DB::getDefault('items', 'delay'),
+ 'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => 0,
+
+ // HTTP Agent item type specific fields.
+ 'url' => DB::getDefault('items', 'url'),
+ 'post_type' => DB::getDefault('items', 'post_type'),
+ 'posts' => DB::getDefault('items', 'posts'),
+ 'headers' => [],
+ 'allow_traps' => DB::getDefault('items', 'allow_traps'),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'),
+
+ // SSH item type specific fields.
+ 'publickey' => DB::getDefault('items', 'publickey'),
+ 'privatekey' => DB::getDefault('items', 'privatekey')
+ ];
- $input = array_intersect_key($input, $this->getInput('visible', []));
+ $input = array_intersect_key($input, $this->getInput('visible', []));
+ $this->getInputs($input, array_keys($input));
- if (array_key_exists('tags', $input)) {
- $input['tags'] = array_filter($input['tags'], function ($tag) {
- return ($tag['tag'] !== '' || $tag['value'] !== '');
- });
- }
+ $options = [];
- try {
- DBstart();
- $delay_flex = $this->getInput('delay_flex', []);
-
- if (array_key_exists('delay', $input) && $delay_flex) {
- $simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
- $time_period_parser = new CTimePeriodParser(['usermacros' => true]);
- $scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]);
-
- foreach ($delay_flex as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
-
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['delay']));
- throw new Exception();
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['period']));
- throw new Exception();
- }
-
- $input['delay'] .= ';'.$interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
+ if (array_key_exists('tags', $input)) {
+ $input['tags'] = prepareItemTags($input['tags']);
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['schedule']));
- throw new Exception();
- }
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8],
+ 'value' => ['type' => API_STRING_UTF8]
+ ]];
- $input['delay'] .= ';'.$interval['schedule'];
- }
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $input['tags'], '/tags', $error)) {
+ error($error);
+ throw new Exception();
}
- }
- if (array_key_exists('headers', $input) && $input['headers']) {
- $input['headers']['value'] += array_fill_keys(array_keys($input['headers']['name']), '');
+ $tag_values = [];
- $headers = [];
- foreach ($input['headers']['name'] as $i => $header_name) {
- if ($header_name !== '' || $input['headers']['value'][$i] !== '') {
- $headers[$header_name] = $input['headers']['value'][$i];
- }
+ foreach ($input['tags'] as $tag) {
+ $tag_values[$tag['tag']][] = $tag['value'];
}
- $input['headers'] = $headers;
+
+ $options['selectTags'] = ['tag', 'value'];
}
- if (array_key_exists('preprocessing', $input) && $input['preprocessing']) {
+ if (array_key_exists('preprocessing', $input)) {
$input['preprocessing'] = normalizeItemPreprocessingSteps($input['preprocessing']);
}
- $items_to_update = [];
- $items = $this->getItemsOrPrototypes();
-
- foreach ($ids as $id) {
- $update_item = [];
+ if (array_key_exists('delay', $input)) {
+ $delay_flex = $this->getInput('delay_flex', []);
- if (array_key_exists('tags', $input)) {
- switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) {
- case ZBX_ACTION_ADD:
- $unique_tags = [];
- foreach (array_merge($items[$id]['tags'], $input['tags']) as $tag) {
- $unique_tags[$tag['tag']][$tag['value']] = $tag;
- }
-
- foreach ($unique_tags as $tags_by_name) {
- foreach ($tags_by_name as $tag) {
- $update_item['tags'][] = $tag;
- }
- }
- break;
-
- case ZBX_ACTION_REPLACE:
- $update_item['tags'] = $input['tags'];
- break;
-
- case ZBX_ACTION_REMOVE:
- $diff_tags = [];
- foreach ($items[$id]['tags'] as $a) {
- foreach ($input['tags'] as $b) {
- if ($a['tag'] === $b['tag'] && $a['value'] === $b['value']) {
- continue 2;
- }
- }
-
- $diff_tags[] = $a;
- }
- $update_item['tags'] = $diff_tags;
- break;
- }
+ if (!isValidCustomIntervals($delay_flex)) {
+ throw new Exception();
}
- if ($prototype || $items[$id]['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- $update_item += $input;
+ $input['delay'] = getDelayWithCustomIntervals($input['delay'], $delay_flex);
+ }
- $type = array_key_exists('type', $input) ? $input['type'] : $items[$id]['type'];
+ if (array_key_exists('headers', $input)) {
+ $input['headers'] = prepareItemHeaders($input['headers']);
+ }
- if ($type != ITEM_TYPE_JMX) {
- unset($update_item['jmx_endpoint']);
- }
+ $itemids = $this->getInput('ids');
- if ($type != ITEM_TYPE_HTTPAGENT && $type != ITEM_TYPE_SCRIPT) {
- unset($update_item['timeout']);
- }
+ if ($item_prototypes) {
+ $db_items = API::ItemPrototype()->get([
+ 'output' => ['type', 'key_', 'value_type', 'templateid', 'authtype', 'allow_traps'],
+ 'selectHosts' => ['status'],
+ 'itemids' => $itemids,
+ 'preservekeys' => true
+ ] + $options);
+ }
+ else {
+ $db_items = API::Item()->get([
+ 'output' => ['type', 'key_', 'value_type', 'templateid', 'flags', 'authtype', 'allow_traps'],
+ 'selectHosts' => ['status'],
+ 'itemids' => $itemids,
+ 'preservekeys' => true
+ ] + $options);
+ }
+
+ $items = [];
+
+ foreach ($itemids as $itemid) {
+ $db_item = $db_items[$itemid];
+
+ if ($item_prototypes) {
+ $db_item['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
}
- else if (array_key_exists('status', $input)) {
- $items_to_update[] = ['itemid' => $id, 'status' => $input['status']];
+
+ $item = array_intersect_key($input, getSanitizedItemFields($input + $db_item));
+
+ if (array_key_exists('tags', $input)) {
+ $item['tags'] = $this->getTagsToUpdate($db_item, $tag_values);
}
- if ($update_item) {
- $items_to_update[] = ['itemid' => $id] + $update_item;
+ if ($item) {
+ $items[] = ['itemid' => $itemid] + $item;
}
}
- if ($items_to_update && !$this->updateItemOrPrototype($items_to_update)) {
- throw new Exception();
+ $result = true;
+
+ if ($items) {
+ if ($item_prototypes) {
+ $response = API::ItemPrototype()->update($items);
+ }
+ else {
+ $response = API::Item()->update($items);
+ }
+
+ if ($response === false) {
+ throw new Exception();
+ }
}
}
catch (Exception $e) {
$result = false;
- CMessageHelper::setErrorTitle($prototype ? _('Cannot update item prototypes') : _('Cannot update items'));
+ CMessageHelper::setErrorTitle(
+ $item_prototypes ? _('Cannot update item prototypes') : _('Cannot update items')
+ );
}
- if (DBend($result)) {
+ if ($result) {
$messages = CMessageHelper::getMessages();
- $output = ['title' => $prototype ? _('Item prototypes updated') : _('Items updated')];
+ $output = ['title' => $item_prototypes ? _('Item prototypes updated') : _('Items updated')];
if (count($messages)) {
$output['messages'] = array_column($messages, 'message');
@@ -371,6 +290,59 @@ class CControllerPopupMassupdateItem extends CController {
}
/**
+ * Get item tags to update or null if no tags to update found.
+ *
+ * @param array $db_item
+ * @param array $tag_values
+ *
+ * @return array
+ */
+ private function getTagsToUpdate(array $db_item, array $tag_values): ?array {
+ $tags = [];
+
+ switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) {
+ case ZBX_ACTION_ADD:
+ foreach ($db_item['tags'] as $db_tag) {
+ if (array_key_exists($db_tag['tag'], $tag_values)
+ && in_array($db_tag['value'], $tag_values[$db_tag['tag']])) {
+ unset($tag_values[$db_tag['tag']][$db_tag['value']]);
+ }
+ }
+
+ foreach ($tag_values as $tag => $values) {
+ foreach ($values as $value) {
+ $tags[] = ['tag' => (string) $tag, 'value' => $value];
+ }
+ }
+
+ $tags = array_merge($db_item['tags'], $tags);
+ break;
+
+ case ZBX_ACTION_REPLACE:
+ foreach ($tag_values as $tag => $values) {
+ foreach ($values as $value) {
+ $tags[] = ['tag' => (string) $tag, 'value' => $value];
+ }
+ }
+
+ CArrayHelper::sort($tags, ['tag', 'value']);
+ CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
+ break;
+
+ case ZBX_ACTION_REMOVE:
+ foreach ($db_item['tags'] as $db_tag) {
+ if (!array_key_exists($db_tag['tag'], $tag_values)
+ || !in_array($db_tag['value'], $tag_values[$db_tag['tag']])) {
+ $tags[] = ['tag' => $db_tag['tag'], 'value' => $db_tag['value']];
+ }
+ }
+ break;
+ }
+
+ return $tags;
+ }
+
+ /**
* Handle item mass update form initialization.
*
* @return CControllerResponse
diff --git a/ui/app/views/js/popup.massupdate.item.js.php b/ui/app/views/js/popup.massupdate.item.js.php
index 21c43937079..aee3728c71a 100644
--- a/ui/app/views/js/popup.massupdate.item.js.php
+++ b/ui/app/views/js/popup.massupdate.item.js.php
@@ -62,68 +62,50 @@
// History mode.
(() => {
- const history_elem = document.querySelector('#history_div');
+ const history_toggle = document.getElementById('history_mode');
- if (!history_elem) {
+ if (!history_toggle) {
return false;
}
- let obj = history_elem;
- if (history_elem.tagName === 'SPAN') {
- obj = history_elem.originalObject;
- }
+ history_toggle.addEventListener('change', () => {
+ const history_input = document.getElementById('history');
- obj
- .querySelector('#history_mode')
- .addEventListener('change', (event) => {
- const history_input = obj.querySelector('#history');
- const state = obj.querySelector('#history_mode_<?= ITEM_STORAGE_OFF ?>').checked;
- if (state) {
- history_input.disabled = true;
- history_input.style.display = 'none';
- }
- else {
- history_input.disabled = false;
- history_input.style.display = '';
- }
- });
+ if (document.getElementById('history_mode_<?= ITEM_STORAGE_OFF ?>').checked) {
+ history_input.style.display = 'none';
+ history_input.disabled = true;
+ }
+ else {
+ history_input.style.display = '';
+ history_input.disabled = false;
+ }
+ });
- obj
- .querySelector('#history_mode')
- .dispatchEvent(new CustomEvent('change', {}));
+ history_toggle.dispatchEvent(new CustomEvent('change'));
})();
// Trends mode.
(() => {
- const trends_elem = document.querySelector('#trends_div');
+ const trends_toggle = document.getElementById('trends_mode');
- if (!trends_elem) {
+ if (!trends_toggle) {
return false;
}
- let obj = trends_elem;
- if (trends_elem.tagName === 'SPAN') {
- obj = trends_elem.originalObject;
- }
+ trends_toggle.addEventListener('change', () => {
+ const trends_input = document.getElementById('trends');
- obj
- .querySelector('#trends_mode')
- .addEventListener('change', (event) => {
- const trends_input = obj.querySelector('#trends');
- const state = obj.querySelector('#trends_mode_<?= ITEM_STORAGE_OFF ?>').checked;
- if (state) {
- trends_input.disabled = true;
- trends_input.style.display = 'none';
- }
- else {
- trends_input.disabled = false;
- trends_input.style.display = '';
- }
- });
+ if (document.getElementById('trends_mode_<?= ITEM_STORAGE_OFF ?>').checked) {
+ trends_input.disabled = true;
+ trends_input.style.display = 'none';
+ }
+ else {
+ trends_input.disabled = false;
+ trends_input.style.display = '';
+ }
+ });
- obj
- .querySelector('#trends_mode')
- .dispatchEvent(new CustomEvent('change', {}));
+ trends_toggle.dispatchEvent(new CustomEvent('change'));
})();
// Custom intervals.
diff --git a/ui/app/views/popup.itemtestedit.view.php b/ui/app/views/popup.itemtestedit.view.php
index a3d88f62cce..09ef6be0457 100644
--- a/ui/app/views/popup.itemtestedit.view.php
+++ b/ui/app/views/popup.itemtestedit.view.php
@@ -274,7 +274,7 @@ $form_grid->addItem([
->setId('time')
),
- ($data['preproc_item'] instanceof CDiscoveryRule)
+ ($data['test_type'] == CControllerPopupItemTestEdit::ZBX_TEST_TYPE_LLD)
? null
: (new CFormField((new CCheckBox('not_supported'))->setLabel(_('Not supported'))))
->addClass(CFormField::ZBX_STYLE_FORM_FIELD_FLUID),
diff --git a/ui/app/views/popup.massupdate.item.php b/ui/app/views/popup.massupdate.item.php
index 61ace107ddf..b54164150d5 100644
--- a/ui/app/views/popup.massupdate.item.php
+++ b/ui/app/views/popup.massupdate.item.php
@@ -302,7 +302,7 @@ $item_form_list
(new CVisibilityBox('visible[trends]', 'trends_div', _('Original')))->setLabel(_('Trend storage period')),
(new CDiv([
(new CRadioButtonList('trends_mode', ITEM_STORAGE_CUSTOM))
- ->addValue(_('Do not keep trends'), ITEM_STORAGE_CUSTOM)
+ ->addValue(_('Do not keep trends'), ITEM_STORAGE_OFF)
->addValue(_('Storage period'), ITEM_STORAGE_CUSTOM)
->setModern(true),
(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
diff --git a/ui/auditacts.php b/ui/auditacts.php
index 6d5b3ed07a0..c73c4d3251e 100644
--- a/ui/auditacts.php
+++ b/ui/auditacts.php
@@ -20,7 +20,6 @@
require_once dirname(__FILE__).'/include/config.inc.php';
-require_once dirname(__FILE__).'/include/audit.inc.php';
require_once dirname(__FILE__).'/include/actions.inc.php';
require_once dirname(__FILE__).'/include/users.inc.php';
diff --git a/ui/disc_prototypes.php b/ui/disc_prototypes.php
index ce990385778..2979e6c040d 100644
--- a/ui/disc_prototypes.php
+++ b/ui/disc_prototypes.php
@@ -245,32 +245,29 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT && itemTypeInterface(getRequest('type')) == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
$valid_input = check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
unset($_REQUEST[$paramsFieldName]);
-// permissions
-$discoveryRule = API::DiscoveryRule()->get([
- 'output' => ['hostid'],
+// Permissions.
+$lld_rules = API::DiscoveryRule()->get([
+ 'output' => ['itemid', 'hostid'],
+ 'selectHosts' => ['status'],
'itemids' => getRequest('parent_discoveryid'),
'editable' => true
]);
-$discoveryRule = reset($discoveryRule);
-if (!$discoveryRule) {
+
+if (!$lld_rules) {
access_deny();
}
-$itemPrototypeId = getRequest('itemid');
-if ($itemPrototypeId) {
+$itemid = getRequest('itemid');
+
+if ($itemid) {
$item_prototypes = API::ItemPrototype()->get([
'output' => [],
- 'itemids' => $itemPrototypeId,
+ 'itemids' => $itemid,
'editable' => true
]);
@@ -279,29 +276,6 @@ if ($itemPrototypeId) {
}
}
-// Convert CR+LF to LF in preprocessing script.
-if (hasRequest('preprocessing')) {
- foreach ($_REQUEST['preprocessing'] as &$step) {
- if ($step['type'] == ZBX_PREPROC_SCRIPT) {
- $step['params'][0] = CRLFtoLF($step['params'][0]);
- }
- }
- unset($step);
-}
-
-$tags = getRequest('tags', []);
-foreach ($tags as $key => $tag) {
- if ($tag['tag'] === '' && $tag['value'] === '') {
- unset($tags[$key]);
- }
- elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
- unset($tags[$key]);
- }
- else {
- unset($tags[$key]['type']);
- }
-}
-
/*
* Actions
*/
@@ -318,244 +292,149 @@ if (hasRequest('delete') && hasRequest('itemid')) {
unset($_REQUEST['itemid'], $_REQUEST['form']);
}
elseif (hasRequest('add') || hasRequest('update')) {
- $result = true;
- DBstart();
-
- $delay = getRequest('delay', DB::getDefault('items', 'delay'));
- $type = getRequest('type', ITEM_TYPE_ZABBIX);
-
- /*
- * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
- * In the end, custom intervals together with "delay" are stored in the "delay" variable.
- */
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
- && hasRequest('delay_flex')) {
- $intervals = [];
- $simple_interval_parser = new CSimpleIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
- $time_period_parser = new CTimePeriodParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
- $scheduling_interval_parser = new CSchedulingIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
-
- foreach (getRequest('delay_flex') as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
-
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['delay']));
- break;
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['period']));
- break;
- }
-
- $intervals[] = $interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
-
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['schedule']));
- break;
- }
+ try {
+ $type = (int) getRequest('type', DB::getDefault('items', 'type'));
+ $key = getRequest('key', DB::getDefault('items', 'key_'));
- $intervals[] = $interval['schedule'];
- }
+ if (isItemExampleKey($type, $key)) {
+ throw new Exception();
}
- if ($intervals) {
- $delay .= ';'.implode(';', $intervals);
+ $delay_flex = getRequest('delay_flex', []);
+
+ if (!isValidCustomIntervals($delay_flex, true)) {
+ throw new Exception();
}
- }
- if ($result) {
- $preprocessing = getRequest('preprocessing', []);
- $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
-
- $item = [
- 'name' => getRequest('name'),
- 'description' => getRequest('description'),
- 'key_' => getRequest('key'),
- 'hostid' => $discoveryRule['hostid'],
- 'interfaceid' => getRequest('interfaceid'),
- 'delay' => $delay,
- 'status' => getRequest('status', ITEM_STATUS_DISABLED),
- 'discover' => getRequest('discover', ZBX_PROTOTYPE_DISCOVER),
- 'type' => getRequest('type'),
- 'snmp_oid' => getRequest('snmp_oid'),
- 'value_type' => getRequest('value_type'),
- 'trapper_hosts' => getRequest('trapper_hosts'),
- 'history' => (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
+ $value_type = (int) getRequest('value_type', DB::getDefault('items', 'value_type'));
+ $trends_default = in_array($value_type, [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])
+ ? DB::getDefault('items', 'trends')
+ : 0;
+
+ $request_method = getRequest('request_method', DB::getDefault('items', 'request_method'));
+ $retrieve_mode_default = $request_method == HTTPCHECK_REQUEST_HEAD
+ ? HTTPTEST_STEP_RETRIEVE_MODE_HEADERS
+ : DB::getDefault('items', 'retrieve_mode');
+
+ $input = [
+ 'name' => getRequest('name', DB::getDefault('items', 'name')),
+ 'type' => $type,
+ 'key_' => $key,
+ 'value_type' => $value_type,
+ 'units' => getRequest('units', DB::getDefault('items', 'units')),
+ 'history' => getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('history', DB::getDefault('items', 'history')),
+ 'trends' => getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
? ITEM_NO_STORAGE_VALUE
- : getRequest('history'),
- 'units' => getRequest('units'),
- 'logtimefmt' => getRequest('logtimefmt'),
- 'valuemapid' => getRequest('valuemapid', 0),
- 'authtype' => getRequest('authtype'),
- 'username' => getRequest('username'),
- 'password' => getRequest('password'),
- 'publickey' => getRequest('publickey'),
- 'privatekey' => getRequest('privatekey'),
- 'params' => getRequest('params'),
- 'ipmi_sensor' => getRequest('ipmi_sensor'),
- 'ruleid' => getRequest('parent_discoveryid')
+ : getRequest('trends', $trends_default),
+ 'valuemapid' => getRequest('valuemapid', 0),
+ 'inventory_link' => getRequest('inventory_link', DB::getDefault('items', 'inventory_link')),
+ 'logtimefmt' => getRequest('logtimefmt', DB::getDefault('items', 'logtimefmt')),
+ 'description' => getRequest('description', DB::getDefault('items', 'description')),
+ 'status' => getRequest('status', ITEM_STATUS_DISABLED),
+ 'discover' => getRequest('discover', DB::getDefault('items', 'discover')),
+ 'tags' => prepareItemTags(getRequest('tags', [])),
+ 'preprocessing' => normalizeItemPreprocessingSteps(getRequest('preprocessing', [])),
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid' => getRequest('interfaceid', 0),
+ 'authtype' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_authtype', DB::getDefault('items', 'authtype'))
+ : getRequest('authtype', DB::getDefault('items', 'authtype')),
+ 'username' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_username', DB::getDefault('items', 'username'))
+ : getRequest('username', DB::getDefault('items', 'username')),
+ 'password' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_password', DB::getDefault('items', 'password'))
+ : getRequest('password', DB::getDefault('items', 'password')),
+ 'params' => getRequest('params', DB::getDefault('items', 'params')),
+ 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
+ 'delay' => getDelayWithCustomIntervals(getRequest('delay', DB::getDefault('items', 'delay')), $delay_flex),
+ 'trapper_hosts' => getRequest('trapper_hosts', DB::getDefault('items', 'trapper_hosts')),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => getRequest('master_itemid', 0),
+
+ // HTTP Agent item type specific fields.
+ 'url' => getRequest('url', DB::getDefault('items', 'url')),
+ 'query_fields' => prepareItemQueryFields(getRequest('query_fields', [])),
+ 'request_method' => $request_method,
+ 'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
+ 'posts' => getRequest('posts', DB::getDefault('items', 'posts')),
+ 'headers' => prepareItemHeaders(getRequest('headers', [])),
+ 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
+ 'follow_redirects' => getRequest('follow_redirects', DB::getDefault('items', 'follow_redirects')),
+ 'retrieve_mode' => getRequest('retrieve_mode', $retrieve_mode_default),
+ 'output_format' => getRequest('output_format', DB::getDefault('items', 'output_format')),
+ 'http_proxy' => getRequest('http_proxy', DB::getDefault('items', 'http_proxy')),
+ 'verify_peer' => getRequest('verify_peer', DB::getDefault('items', 'verify_peer')),
+ 'verify_host' => getRequest('verify_host', DB::getDefault('items', 'verify_host')),
+ 'ssl_cert_file' => getRequest('ssl_cert_file', DB::getDefault('items', 'ssl_cert_file')),
+ 'ssl_key_file' => getRequest('ssl_key_file', DB::getDefault('items', 'ssl_key_file')),
+ 'ssl_key_password' => getRequest('ssl_key_password', DB::getDefault('items', 'ssl_key_password')),
+ 'allow_traps' => getRequest('allow_traps', DB::getDefault('items', 'allow_traps')),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => getRequest('ipmi_sensor', DB::getDefault('items', 'ipmi_sensor')),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => getRequest('jmx_endpoint', DB::getDefault('items', 'jmx_endpoint')),
+
+ // Script item type specific fields.
+ 'parameters' => prepareItemParameters(getRequest('parameters', [])),
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => getRequest('snmp_oid', DB::getDefault('items', 'snmp_oid')),
+
+ // SSH item type specific fields.
+ 'publickey' => getRequest('publickey', DB::getDefault('items', 'publickey')),
+ 'privatekey' => getRequest('privatekey', DB::getDefault('items', 'privatekey'))
];
- switch ($item['type']) {
- case ITEM_TYPE_SCRIPT:
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
-
- $item = prepareScriptItemFormData($script_item) + $item;
- break;
-
- case ITEM_TYPE_JMX:
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- break;
-
- case ITEM_TYPE_DEPENDENT:
- $item['master_itemid'] = getRequest('master_itemid');
- break;
-
- case ITEM_TYPE_HTTPAGENT:
- $http_item = [
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
- 'url' => getRequest('url'),
- 'query_fields' => getRequest('query_fields', []),
- 'posts' => getRequest('posts'),
- 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
- 'follow_redirects' => (int) getRequest('follow_redirects'),
- 'post_type' => (int) getRequest('post_type'),
- 'http_proxy' => getRequest('http_proxy'),
- 'headers' => getRequest('headers', []),
- 'retrieve_mode' => (int) getRequest('retrieve_mode'),
- 'request_method' => (int) getRequest('request_method'),
- 'output_format' => (int) getRequest('output_format'),
- 'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
- 'ssl_cert_file' => getRequest('ssl_cert_file'),
- 'ssl_key_file' => getRequest('ssl_key_file'),
- 'ssl_key_password' => getRequest('ssl_key_password'),
- 'verify_peer' => (int) getRequest('verify_peer'),
- 'verify_host' => (int) getRequest('verify_host'),
- 'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
- 'username' => getRequest('http_username', ''),
- 'password' => getRequest('http_password', '')
- ];
- break;
- }
+ $result = true;
- if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
- $item['trends'] = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('trends');
- }
+ if (hasRequest('add')) {
+ $item = [
+ 'hostid' => $lld_rules[0]['hostid'],
+ 'ruleid' => $lld_rules[0]['itemid']
+ ];
- if (hasRequest('update')) {
- $itemId = getRequest('itemid');
-
- $db_item = API::ItemPrototype()->get([
- 'output' => ['type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history',
- 'trends', 'status', 'value_type', 'trapper_hosts', 'units',
- 'logtimefmt', 'templateid', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'interfaceid', 'description', 'jmx_endpoint',
- 'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
- 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format',
- 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps',
- 'discover', 'parameters'
- ],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => [$itemId]
+ $item += getSanitizedItemFields($input + [
+ 'templateid' => '0',
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hosts' => $lld_rules[0]['hosts']
]);
- $db_item = $db_item[0];
-
- if ($item['type'] == ITEM_TYPE_HTTPAGENT && $db_item['templateid'] == 0) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
-
- if ($db_item['type'] == $item['type']) {
- $item = CArrayHelper::unsetEqualValues($item, $db_item);
- }
-
- $item['itemid'] = $itemId;
-
- if ($db_item['preprocessing'] !== $preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
+ $response = API::ItemPrototype()->create($item);
- function parameters_equal(array $stored_parameters, array $input_parameters): bool {
- return (array_column($stored_parameters, 'value') == array_column($input_parameters, 'value')
- && array_column($stored_parameters, 'name') == array_column($input_parameters, 'name'));
+ if ($response === false) {
+ throw new Exception();
}
+ }
- if (getRequest('type') == ITEM_TYPE_SCRIPT && $db_item['type'] == getRequest('type')
- && parameters_equal($db_item['parameters'], $item['parameters'])) {
- unset($item['parameters']);
- }
-
- CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
- CArrayHelper::sort($tags, ['tag', 'value']);
-
- if (array_values($db_item['tags']) !== array_values($tags)) {
- $item['tags'] = $tags;
- }
+ if (hasRequest('update')) {
+ $db_items = API::ItemPrototype()->get([
+ 'output' => ['templateid', 'type', 'key_', 'value_type', 'authtype', 'allow_traps'],
+ 'itemids' => $itemid
+ ]);
- if ($db_item['templateid'] != 0) {
- $allowed_fields = array_fill_keys([
- 'itemid', 'delay', 'delay_flex', 'history', 'trends', 'history_mode', 'trends_mode', 'allow_traps',
- 'description', 'status', 'discover', 'tags'
- ], true);
-
- if ($db_item['type'] != ITEM_TYPE_HTTPAGENT) {
- $allowed_fields += array_fill_keys([
- 'authtype', 'username', 'password', 'params', 'publickey', 'privatekey', 'interfaceid'
- ], true);
- }
-
- foreach ($item as $field => $value) {
- if (!array_key_exists($field, $allowed_fields)) {
- unset($item[$field]);
- }
- }
- }
+ $item = getSanitizedItemFields($input + $db_items[0] + [
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hosts' => $lld_rules[0]['hosts']
+ ]);
- $result = API::ItemPrototype()->update($item);
- }
- else {
- if (getRequest('type') == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
+ $response = API::ItemPrototype()->update(['itemid' => $itemid] + $item);
- if ($preprocessing) {
- $item['preprocessing'] = $preprocessing;
+ if ($response === false) {
+ throw new Exception();
}
-
- $item['tags'] = $tags;
-
- $result = API::ItemPrototype()->create($item);
}
}
-
- $result = DBend($result);
+ catch (Exception $e) {
+ $result = false;
+ }
if (hasRequest('add')) {
show_messages($result, _('Item prototype added'), _('Cannot add item prototype'));
@@ -757,7 +636,7 @@ else {
$data = [
'form' => getRequest('form'),
'parent_discoveryid' => getRequest('parent_discoveryid'),
- 'hostid' => $discoveryRule['hostid'],
+ 'hostid' => $lld_rules[0]['hostid'],
'sort' => $sortField,
'sortorder' => $sortOrder,
'context' => getRequest('context')
diff --git a/ui/host_discovery.php b/ui/host_discovery.php
index 973cd4bc8b6..b23bdbfaf0d 100644
--- a/ui/host_discovery.php
+++ b/ui/host_discovery.php
@@ -238,11 +238,6 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
@@ -420,13 +415,21 @@ elseif (hasRequest('add') || hasRequest('update')) {
$delay = getRequest('delay', DB::getDefault('items', 'delay'));
$type = getRequest('type', ITEM_TYPE_ZABBIX);
+ $item_key = getRequest('key', '');
+
+ if (($type == ITEM_TYPE_DB_MONITOR && $item_key === ZBX_DEFAULT_KEY_DB_MONITOR)
+ || ($type == ITEM_TYPE_SSH && $item_key === ZBX_DEFAULT_KEY_SSH)
+ || ($type == ITEM_TYPE_TELNET && $item_key === ZBX_DEFAULT_KEY_TELNET)) {
+ error(_('Check the key, please. Default example was passed.'));
+ $result = false;
+ }
/*
* "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
* In the end, custom intervals together with "delay" are stored in the "delay" variable.
*/
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
+ if ($result && $type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
+ && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($item_key, 'mqtt.get', 8) !== 0)
&& hasRequest('delay_flex')) {
$intervals = [];
$simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
@@ -475,54 +478,14 @@ elseif (hasRequest('add') || hasRequest('update')) {
if ($result) {
$preprocessing = getRequest('preprocessing', []);
-
- foreach ($preprocessing as &$step) {
- switch ($step['type']) {
- case ZBX_PREPROC_PROMETHEUS_TO_JSON:
- $step['params'] = trim($step['params'][0]);
- break;
-
- case ZBX_PREPROC_XPATH:
- case ZBX_PREPROC_JSONPATH:
- case ZBX_PREPROC_VALIDATE_NOT_REGEX:
- case ZBX_PREPROC_ERROR_FIELD_JSON:
- case ZBX_PREPROC_ERROR_FIELD_XML:
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- case ZBX_PREPROC_SCRIPT:
- $step['params'] = $step['params'][0];
- break;
-
- case ZBX_PREPROC_REGSUB:
- case ZBX_PREPROC_STR_REPLACE:
- $step['params'] = implode("\n", $step['params']);
- break;
-
- case ZBX_PREPROC_CSV_TO_JSON:
- if (!array_key_exists(2, $step['params'])) {
- $step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
- }
- $step['params'] = implode("\n", $step['params']);
- break;
-
- default:
- $step['params'] = '';
- }
-
- $step += [
- 'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
- 'error_handler_params' => ''
- ];
-
- unset($step['sortorder']);
- }
- unset($step);
+ $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
$newItem = [
'itemid' => getRequest('itemid'),
'interfaceid' => getRequest('interfaceid', 0),
'name' => getRequest('name'),
'description' => getRequest('description'),
- 'key_' => getRequest('key'),
+ 'key_' => $item_key,
'hostid' => getRequest('hostid'),
'delay' => $delay,
'status' => getRequest('status', ITEM_STATUS_DISABLED),
diff --git a/ui/include/audit.inc.php b/ui/include/audit.inc.php
deleted file mode 100644
index 94629e946d5..00000000000
--- a/ui/include/audit.inc.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 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.
-**/
-
-
-// function add_audit($action, $resourcetype, $note) {
-// if (mb_strlen($note) > 128) {
-// $note = mb_substr($note, 0, 125).'...';
-// }
-
-// $values = [
-// 'userid' => CWebUser::$data['userid'],
-// 'clock' => time(),
-// 'ip' => substr(CWebUser::getIp(), 0, 39),
-// 'action' => $action,
-// 'resourcetype' => $resourcetype,
-// 'note' => $note
-// ];
-
-// try {
-// DB::insert('auditlog', [$values]);
-// return true;
-// }
-// catch (DBException $e) {
-// return false;
-// }
-// }
diff --git a/ui/include/classes/api/CAudit.php b/ui/include/classes/api/CAudit.php
index b846e380071..1d15f35b17c 100644
--- a/ui/include/classes/api/CAudit.php
+++ b/ui/include/classes/api/CAudit.php
@@ -123,6 +123,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'icon_map',
self::RESOURCE_IMAGE => 'images',
self::RESOURCE_ITEM => 'items',
+ self::RESOURCE_ITEM_PROTOTYPE => 'items',
self::RESOURCE_IT_SERVICE => 'services',
self::RESOURCE_MACRO => 'globalmacro',
self::RESOURCE_MAINTENANCE => 'maintenances',
@@ -130,6 +131,7 @@ class CAudit {
self::RESOURCE_MODULE => 'module',
self::RESOURCE_PROXY => 'hosts',
self::RESOURCE_REGEXP => 'regexps',
+ self::RESOURCE_SCENARIO => 'httptest',
self::RESOURCE_SCHEDULED_REPORT => 'report',
self::RESOURCE_SCRIPT => 'scripts',
self::RESOURCE_SETTINGS => 'config',
@@ -173,6 +175,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'name',
self::RESOURCE_IMAGE => 'name',
self::RESOURCE_ITEM => 'name',
+ self::RESOURCE_ITEM_PROTOTYPE => 'name',
self::RESOURCE_IT_SERVICE => 'name',
self::RESOURCE_MACRO => 'macro',
self::RESOURCE_MAINTENANCE => 'name',
@@ -180,6 +183,7 @@ class CAudit {
self::RESOURCE_MODULE => 'id',
self::RESOURCE_PROXY => 'host',
self::RESOURCE_REGEXP => 'name',
+ self::RESOURCE_SCENARIO => 'name',
self::RESOURCE_SCHEDULED_REPORT => 'name',
self::RESOURCE_SCRIPT => 'name',
self::RESOURCE_SETTINGS => null,
@@ -212,6 +216,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'iconmap',
self::RESOURCE_IMAGE => 'image',
self::RESOURCE_ITEM => 'item',
+ self::RESOURCE_ITEM_PROTOTYPE => 'itemprototype',
self::RESOURCE_IT_SERVICE => 'service',
self::RESOURCE_MACRO => 'usermacro',
self::RESOURCE_MAINTENANCE => 'maintenance',
@@ -246,6 +251,44 @@ class CAudit {
'paths' => ['hostprototype.macros.value'],
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
],
+ self::RESOURCE_ITEM => [
+ [
+ 'paths' => ['item.password'],
+ 'conditions' => [
+ [
+ 'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX
+ ]
+ ],
+ [
+ 'type' => ITEM_TYPE_HTTPAGENT,
+ 'authtype' => [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ]
+ ]
+ ]
+ ],
+ ['paths' => ['item.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
+ ],
+ self::RESOURCE_ITEM_PROTOTYPE => [
+ [
+ 'paths' => ['itemprototype.password'],
+ 'conditions' => [
+ [
+ 'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX
+ ]
+ ],
+ [
+ 'type' => ITEM_TYPE_HTTPAGENT,
+ 'authtype' => [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ]
+ ]
+ ]
+ ],
+ ['paths' => ['itemprototype.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
+ ],
self::RESOURCE_MACRO => [
'paths' => ['usermacro.value'],
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
@@ -312,6 +355,12 @@ class CAudit {
'hostprototype.tags' => 'host_tag',
'hostprototype.templates' => 'hosts_templates',
'iconmap.mappings' => 'icon_mapping',
+ 'item.parameters' => 'item_parameter',
+ 'item.preprocessing' => 'item_preproc',
+ 'item.tags' => 'item_tag',
+ 'itemprototype.parameters' => 'item_parameter',
+ 'itemprototype.preprocessing' => 'item_preproc',
+ 'itemprototype.tags' => 'item_tag',
'maintenance.groups' => 'maintenances_groups',
'maintenance.hosts' => 'maintenances_hosts',
'maintenance.tags' => 'maintenance_tag',
@@ -390,6 +439,12 @@ class CAudit {
'hostprototype.tags' => 'hosttagid',
'hostprototype.templates' => 'hosttemplateid',
'iconmap.mappings' => 'iconmappingid',
+ 'item.parameters' => 'item_parameterid',
+ 'item.preprocessing' => 'item_preprocid',
+ 'item.tags' => 'itemtagid',
+ 'itemprototype.parameters' => 'item_parameterid',
+ 'itemprototype.preprocessing' => 'item_preprocid',
+ 'itemprototype.tags' => 'itemtagid',
'maintenance.groups' => 'maintenance_groupid',
'maintenance.hosts' => 'maintenance_hostid',
'maintenance.tags' => 'maintenancetagid',
@@ -640,27 +695,60 @@ class CAudit {
}
$object_path = self::getLastObjectPath($path);
+ $abstract_path = self::getAbstractPath($path);
- if (!in_array(self::getAbstractPath($path), self::MASKED_PATHS[$resource]['paths'])) {
+ $rules = [];
+
+ if (array_key_exists('paths', self::MASKED_PATHS[$resource])) {
+ if (in_array($abstract_path, self::MASKED_PATHS[$resource]['paths'])) {
+ $rules = self::MASKED_PATHS[$resource];
+ }
+ }
+ else {
+ foreach (self::MASKED_PATHS[$resource] as $_rules) {
+ if (in_array($abstract_path, $_rules['paths'])) {
+ $rules = $_rules;
+ break;
+ }
+ }
+ }
+
+ if (!$rules) {
return false;
}
- if (!array_key_exists('conditions', self::MASKED_PATHS[$resource])) {
+ if (!array_key_exists('conditions', $rules)) {
return true;
}
- $all_conditions = count(self::MASKED_PATHS[$resource]['conditions']);
- $true_conditions = 0;
+ $or_conditions = $rules['conditions'];
+
+ if (!array_key_exists(0, $or_conditions)) {
+ $or_conditions = [$or_conditions];
+ }
+
+ foreach ($or_conditions as $and_conditions) {
+ $all_conditions = count($and_conditions);
+ $true_conditions = 0;
- foreach (self::MASKED_PATHS[$resource]['conditions'] as $condition_key => $value) {
- $condition_path = $object_path.'.'.$condition_key;
+ foreach ($and_conditions as $condition_key => $value) {
+ $condition_path = $object_path.'.'.$condition_key;
- if (array_key_exists($condition_path, $object) && $object[$condition_path] == $value) {
- $true_conditions++;
+ if (array_key_exists($condition_path, $object)) {
+ $values = is_array($value) ? $value : [$value];
+
+ if (in_array($object[$condition_path], $values)) {
+ $true_conditions++;
+ }
+ }
+ }
+
+ if ($true_conditions == $all_conditions) {
+ return true;
}
}
- return ($true_conditions == $all_conditions);
+ return false;
}
/**
@@ -739,6 +827,11 @@ class CAudit {
return false;
}
+ if ($schema_fields[$field_name]['type'] === DB::FIELD_TYPE_ID && $schema_fields[$field_name]['null']
+ && $value == 0) {
+ return true;
+ }
+
if (!array_key_exists('default', $schema_fields[$field_name])) {
return false;
}
@@ -824,19 +917,21 @@ class CAudit {
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_ADD];
}
- if (self::isDefaultValue($resource, $path, $value)) {
+ if (self::isValueToMask($resource, $path, $object)) {
+ $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
continue;
}
- if (self::isValueToMask($resource, $path, $object)) {
- $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
+ if (self::isDefaultValue($resource, $path, $value)) {
+ continue;
}
- elseif (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
+
+ if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_ADD];
+ continue;
}
- else {
- $result[$path] = [self::DETAILS_ACTION_ADD, $value];
- }
+
+ $result[$path] = [self::DETAILS_ACTION_ADD, $value];
}
return $result;
@@ -880,42 +975,46 @@ class CAudit {
}
foreach ($object as $path => $value) {
+ $is_value_to_mask = self::isValueToMask($resource, $path, $object);
$db_value = array_key_exists($path, $db_object) ? $db_object[$path] : null;
if ($db_value === null) {
+ $is_value_to_mask = self::isValueToMask($resource, $path, $object);
+
+ if ($is_value_to_mask) {
+ $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
+ continue;
+ }
+
if (self::isDefaultValue($resource, $path, $value)) {
continue;
}
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_ADD];
+ continue;
}
- else {
- $result[$path] = [
- self::DETAILS_ACTION_ADD,
- self::isValueToMask($resource, $path, $object) ? ZBX_SECRET_MASK : $value
- ];
- }
+
+ $result[$path] = [self::DETAILS_ACTION_ADD, $value];
}
else {
- $is_mask_value = self::isValueToMask($resource, $path, $object);
- $is_mask_db_value = self::isValueToMask($resource, $path, $db_object);
+ $is_db_value_to_mask = self::isValueToMask($resource, $path, $db_object);
- if ($value != $db_value || $is_mask_value || $is_mask_db_value) {
+ if ($value != $db_value || $is_value_to_mask || $is_db_value_to_mask) {
if (self::isNestedObjectProperty($path)) {
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_UPDATE];
}
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_UPDATE];
+ continue;
}
- else {
- $result[$path] = [
- self::DETAILS_ACTION_UPDATE,
- $is_mask_value ? ZBX_SECRET_MASK : $value,
- $is_mask_db_value ? ZBX_SECRET_MASK : $db_value
- ];
- }
+
+ $result[$path] = [
+ self::DETAILS_ACTION_UPDATE,
+ $is_value_to_mask ? ZBX_SECRET_MASK : $value,
+ $is_db_value_to_mask ? ZBX_SECRET_MASK : $db_value
+ ];
}
}
}
diff --git a/ui/include/classes/api/CItemTypeFactory.php b/ui/include/classes/api/CItemTypeFactory.php
new file mode 100644
index 00000000000..6d03e84efe4
--- /dev/null
+++ b/ui/include/classes/api/CItemTypeFactory.php
@@ -0,0 +1,97 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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.
+**/
+
+final class CItemTypeFactory {
+
+ /**
+ * An array of created object instances.
+ *
+ * @param array
+ */
+ private static $instances = [];
+
+ /**
+ * @param int $type
+ *
+ * @return CItemType
+ *
+ * @throws APIException
+ */
+ public static function getObject(int $type): CItemType {
+ if (array_key_exists($type, self::$instances)) {
+ return self::$instances[$type];
+ }
+
+ switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ return self::$instances[$type] = new CItemTypeZabbix();
+
+ case ITEM_TYPE_TRAPPER:
+ return self::$instances[$type] = new CItemTypeTrapper();
+
+ case ITEM_TYPE_SIMPLE:
+ return self::$instances[$type] = new CItemTypeSimple();
+
+ case ITEM_TYPE_INTERNAL:
+ return self::$instances[$type] = new CItemTypeInternal();
+
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return self::$instances[$type] = new CItemTypeZabbixActive();
+
+ case ITEM_TYPE_EXTERNAL:
+ return self::$instances[$type] = new CItemTypeExternal();
+
+ case ITEM_TYPE_DB_MONITOR:
+ return self::$instances[$type] = new CItemTypeDbMonitor();
+
+ case ITEM_TYPE_IPMI:
+ return self::$instances[$type] = new CItemTypeIpmi();
+
+ case ITEM_TYPE_SSH:
+ return self::$instances[$type] = new CItemTypeSsh();
+
+ case ITEM_TYPE_TELNET:
+ return self::$instances[$type] = new CItemTypeTelnet();
+
+ case ITEM_TYPE_CALCULATED:
+ return self::$instances[$type] = new CItemTypeCalculated();
+
+ case ITEM_TYPE_JMX:
+ return self::$instances[$type] = new CItemTypeJmx();
+
+ case ITEM_TYPE_SNMPTRAP:
+ return self::$instances[$type] = new CItemTypeSnmpTrap();
+
+ case ITEM_TYPE_DEPENDENT:
+ return self::$instances[$type] = new CItemTypeDependent();
+
+ case ITEM_TYPE_HTTPAGENT:
+ return self::$instances[$type] = new CItemTypeHttpAgent();
+
+ case ITEM_TYPE_SNMP:
+ return self::$instances[$type] = new CItemTypeSnmp();
+
+ case ITEM_TYPE_SCRIPT:
+ return self::$instances[$type] = new CItemTypeScript();
+ }
+
+ throw new APIException(ZBX_API_ERROR_INTERNAL, 'Incorrect item type.');
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemType.php b/ui/include/classes/api/item_types/CItemType.php
new file mode 100644
index 00000000000..248139fcff6
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemType.php
@@ -0,0 +1,470 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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.
+**/
+
+abstract class CItemType {
+
+ /**
+ * Item type.
+ *
+ * @var int|null
+ */
+ const TYPE = null;
+
+ /**
+ * Field names of specific type.
+ *
+ * @var array
+ */
+ const FIELD_NAMES = [
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
+ ];
+
+ /**
+ * @param array $item
+ *
+ * @return array
+ */
+ abstract public static function getCreateValidationRules(array $item): array;
+
+ /**
+ * @param array $db_item
+ *
+ * @return array
+ */
+ abstract public static function getUpdateValidationRules(array $db_item): array;
+
+ /**
+ * @param array $db_item
+ *
+ * @return array
+ */
+ abstract public static function getUpdateValidationRulesInherited(array $db_item): array;
+
+ /**
+ * @return array
+ */
+ abstract public static function getUpdateValidationRulesDiscovered(): array;
+
+ /**
+ * @param string $field_name
+ * @param array $item
+ *
+ * @return array
+ */
+ final protected static function getCreateFieldRule(string $field_name, array $item): array {
+ $is_item_prototype = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ switch (static::TYPE) {
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_EXTERNAL:
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ default:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 'type' => API_ID, 'flags' => API_REQUIRED],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+ }
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST]), 'default' => DB::getDefault('items', 'authtype')];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]), 'default' => DB::getDefault('items', 'authtype')];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')]
+ ]];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')]
+ ]];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => API_REQUIRED | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'in' => '1:'.SEC_PER_MIN, 'length' => DB::getFieldLength('items', 'timeout')];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) != 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ * @param array $db_item
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRule(string $field_name, array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ switch (static::TYPE) {
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_EXTERNAL:
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ default:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+ }
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY])];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')]
+ ]];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')]
+ ]];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'in' => '1:'.SEC_PER_MIN, 'length' => DB::getFieldLength('items', 'timeout')];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) !== 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ * @param array $db_item
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRuleInherited(string $field_name, array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY])];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ case ITEM_TYPE_SCRIPT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) !== 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRuleDiscovered(string $field_name): array {
+ switch ($field_name) {
+ case 'interfaceid':
+ case 'authtype':
+ case 'username':
+ case 'password':
+ case 'params':
+ case 'timeout':
+ case 'delay':
+ case 'trapper_hosts':
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED];
+ }
+ }
+
+ /**
+ * @return array
+ */
+ final public static function getDefaultValidationRules(): array {
+ return [
+ // The fields used for multiple item types.
+ 'interfaceid' => ['type' => API_ID, 'in' => '0'],
+ 'authtype' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'authtype')],
+ 'username' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')],
+ 'password' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')],
+ 'params' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'params')],
+ 'timeout' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'timeout')],
+ 'delay' => ['type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')],
+ 'trapper_hosts' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')],
+
+ // Dependent item type specific fields.
+ 'master_itemid' => ['type' => API_ID, 'in' => '0'],
+
+ // HTTP Agent item type specific fields.
+ 'url' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'url')],
+ 'query_fields' => ['type' => API_OBJECTS, 'length' => 0],
+ 'request_method' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'request_method')],
+ 'post_type' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'post_type')],
+ 'posts' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'posts')],
+ 'headers' => ['type' => API_OBJECT, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'in' => DB::getDefault('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'follow_redirects')],
+ 'retrieve_mode' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'retrieve_mode')],
+ 'output_format' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'output_format')],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'http_proxy')],
+ 'verify_peer' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'verify_peer')],
+ 'verify_host' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'verify_host')],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_key_password')],
+ 'allow_traps' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'allow_traps')],
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ipmi_sensor')],
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'jmx_endpoint')],
+
+ // Script item type specific fields.
+ 'parameters' => ['type' => API_OBJECTS, 'length' => 0],
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'snmp_oid')],
+
+ // SSH item type specific fields.
+ 'publickey' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')],
+ 'privatekey' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeCalculated.php b/ui/include/classes/api/item_types/CItemTypeCalculated.php
new file mode 100644
index 00000000000..d93668d90f2
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeCalculated.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeCalculated extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_CALCULATED;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeDbMonitor.php b/ui/include/classes/api/item_types/CItemTypeDbMonitor.php
new file mode 100644
index 00000000000..70eddaa8ea3
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeDbMonitor.php
@@ -0,0 +1,80 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeDbMonitor extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_DB_MONITOR;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['username', 'password', 'params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeDependent.php b/ui/include/classes/api/item_types/CItemTypeDependent.php
new file mode 100644
index 00000000000..1f2c125d90b
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeDependent.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeDependent extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_DEPENDENT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['master_itemid'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'master_itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'master_itemid' => ['type' => API_ID]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'master_itemid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'master_itemid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED]
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeExternal.php b/ui/include/classes/api/item_types/CItemTypeExternal.php
new file mode 100644
index 00000000000..0670afdb277
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeExternal.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeExternal extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_EXTERNAL;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeHttpAgent.php b/ui/include/classes/api/item_types/CItemTypeHttpAgent.php
new file mode 100644
index 00000000000..750547ef2a3
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeHttpAgent.php
@@ -0,0 +1,187 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeHttpAgent extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_HTTPAGENT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['url', 'query_fields', 'request_method', 'post_type', 'posts', 'headers', 'status_codes',
+ 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', 'interfaceid', 'authtype', 'username',
+ 'password', 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'timeout',
+ 'delay', 'allow_traps', 'trapper_hosts'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ $is_item_prototype = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ return [
+ 'url' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')],
+ 'query_fields' => ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'request_method' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD]), 'default' => DB::getDefault('items', 'request_method')],
+ 'post_type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]), 'default' => DB::getDefault('items', 'post_type')],
+ 'posts' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_RAW], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_JSON], 'type' => API_JSON, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'macros_n' => ['{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}', '{ITEM.KEY}', '{ITEM.KEY.ORIG}'], 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_XML], 'type' => API_XML, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'posts')]
+ ]],
+ 'headers' => ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])],
+ 'retrieve_mode' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'request_method', 'in' => HTTPCHECK_REQUEST_HEAD], 'type' => API_INT32, 'in' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS],
+ ['else' => true, 'type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH])]
+ ]],
+ 'output_format' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')],
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'authtype' => self::getCreateFieldRule('authtype', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'verify_peer' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])],
+ 'verify_host' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')],
+ 'timeout' => self::getCreateFieldRule('timeout', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON]), 'default' => DB::getDefault('items', 'allow_traps')],
+ 'trapper_hosts' => self::getCreateFieldRule('trapper_hosts', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ return [
+ 'url' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return $db_item['type'] != ITEM_TYPE_HTTPAGENT;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')]
+ ]],
+ 'query_fields' => ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'request_method' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD])],
+ 'post_type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])],
+ 'posts' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_RAW], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_JSON], 'type' => API_JSON, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'macros_n' => ['{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}', '{ITEM.KEY}', '{ITEM.KEY.ORIG}'], 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_XML], 'type' => API_XML, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'posts')]
+ ]],
+ 'headers' => ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])],
+ 'retrieve_mode' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'request_method', 'in' => HTTPCHECK_REQUEST_HEAD], 'type' => API_INT32, 'in' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS],
+ ['else' => true, 'type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH])]
+ ]],
+ 'output_format' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')],
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRule('authtype', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'verify_peer' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])],
+ 'verify_host' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')],
+ 'timeout' => self::getUpdateFieldRule('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])],
+ 'trapper_hosts' => self::getUpdateFieldRule('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'url' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'query_fields' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'request_method' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'post_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'posts' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'headers' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'status_codes' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'follow_redirects' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'retrieve_mode' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'output_format' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'http_proxy' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRuleInherited('authtype', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'verify_peer' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'verify_host' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_cert_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_key_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_key_password' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'timeout' => self::getUpdateFieldRuleInherited('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])],
+ 'trapper_hosts' => self::getUpdateFieldRuleInherited('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'url' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'query_fields' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'request_method' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'post_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'posts' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'headers' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'status_codes' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'follow_redirects' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'retrieve_mode' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'output_format' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'http_proxy' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'authtype' => self::getUpdateFieldRuleDiscovered('authtype'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'verify_peer' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'verify_host' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_cert_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_key_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_key_password' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'timeout' => self::getUpdateFieldRuleDiscovered('timeout'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay'),
+ 'allow_traps' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'trapper_hosts' => self::getUpdateFieldRuleDiscovered('trapper_hosts')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeInternal.php b/ui/include/classes/api/item_types/CItemTypeInternal.php
new file mode 100644
index 00000000000..8b49fdf252a
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeInternal.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeInternal extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_INTERNAL;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeIpmi.php b/ui/include/classes/api/item_types/CItemTypeIpmi.php
new file mode 100644
index 00000000000..ef10aa20e06
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeIpmi.php
@@ -0,0 +1,82 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeIpmi extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_IPMI;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'ipmi_sensor', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'ipmi_sensor' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'key_', 'in' => 'ipmi.get'], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ipmi_sensor')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'ipmi_sensor')]
+ ]],
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'ipmi_sensor' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'key_', 'in' => 'ipmi.get'], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ipmi_sensor')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'ipmi_sensor')]
+ ]],
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'ipmi_sensor' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'ipmi_sensor' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeJmx.php b/ui/include/classes/api/item_types/CItemTypeJmx.php
new file mode 100644
index 00000000000..bc4d1876ec5
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeJmx.php
@@ -0,0 +1,84 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeJmx extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_JMX;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'jmx_endpoint', 'username', 'password', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint'), 'default' => ZBX_DEFAULT_JMX_ENDPOINT],
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint')],
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint')],
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'jmx_endpoint' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeScript.php b/ui/include/classes/api/item_types/CItemTypeScript.php
new file mode 100644
index 00000000000..37905d0e3b6
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeScript.php
@@ -0,0 +1,86 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeScript extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SCRIPT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['parameters', 'params', 'timeout', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]],
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'timeout' => self::getCreateFieldRule('timeout', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]],
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'timeout' => self::getUpdateFieldRule('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'parameters' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'timeout' => self::getUpdateFieldRuleInherited('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'parameters' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'timeout' => self::getUpdateFieldRuleDiscovered('timeout'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSimple.php b/ui/include/classes/api/item_types/CItemTypeSimple.php
new file mode 100644
index 00000000000..dcee97f0622
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSimple.php
@@ -0,0 +1,80 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeSimple extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SIMPLE;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'username', 'password', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSnmp.php b/ui/include/classes/api/item_types/CItemTypeSnmp.php
new file mode 100644
index 00000000000..50cd9fbbbc8
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSnmp.php
@@ -0,0 +1,76 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeSnmp extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SNMP;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'snmp_oid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'snmp_oid')],
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'snmp_oid')],
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'snmp_oid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'snmp_oid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php b/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php
new file mode 100644
index 00000000000..57532475a5e
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeSnmpTrap extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SNMPTRAP;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSsh.php b/ui/include/classes/api/item_types/CItemTypeSsh.php
new file mode 100644
index 00000000000..b100be940d8
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSsh.php
@@ -0,0 +1,116 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeSsh extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SSH;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'authtype', 'username', 'publickey', 'privatekey', 'password', 'params',
+ 'delay'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'authtype' => self::getCreateFieldRule('authtype', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRule('authtype', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRuleInherited('authtype', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'authtype' => self::getUpdateFieldRuleDiscovered('authtype'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'publickey' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'privatekey' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeTelnet.php b/ui/include/classes/api/item_types/CItemTypeTelnet.php
new file mode 100644
index 00000000000..d39fddaf6fe
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeTelnet.php
@@ -0,0 +1,84 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeTelnet extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_TELNET;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'username', 'password', 'params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeTrapper.php b/ui/include/classes/api/item_types/CItemTypeTrapper.php
new file mode 100644
index 00000000000..366c8ad9450
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeTrapper.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeTrapper extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_TRAPPER;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['trapper_hosts'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'trapper_hosts' => self::getCreateFieldRule('trapper_hosts', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRule('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRuleInherited('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRuleDiscovered('trapper_hosts')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeZabbix.php b/ui/include/classes/api/item_types/CItemTypeZabbix.php
new file mode 100644
index 00000000000..ffc79c9c26e
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeZabbix.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeZabbix extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_ZABBIX;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeZabbixActive.php b/ui/include/classes/api/item_types/CItemTypeZabbixActive.php
new file mode 100644
index 00000000000..50216deb8f2
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeZabbixActive.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 CItemTypeZabbixActive extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_ZABBIX_ACTIVE;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/managers/CDiscoveryRuleManager.php b/ui/include/classes/api/managers/CDiscoveryRuleManager.php
index ccce03b65e5..b75efff2f5c 100644
--- a/ui/include/classes/api/managers/CDiscoveryRuleManager.php
+++ b/ui/include/classes/api/managers/CDiscoveryRuleManager.php
@@ -34,7 +34,7 @@ class CDiscoveryRuleManager {
$parent_itemids = $ruleids;
$child_ruleids = [];
do {
- $db_items = DBselect('SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', $parent_itemids));
+ $db_items = DBselect('SELECT i.itemid FROM items i WHERE '.dbConditionId('i.templateid', $parent_itemids));
$parent_itemids = [];
while ($db_item = DBfetch($db_items)) {
$parent_itemids[$db_item['itemid']] = $db_item['itemid'];
@@ -45,18 +45,15 @@ class CDiscoveryRuleManager {
$ruleids = array_merge($ruleids, $child_ruleids);
// Delete item prototypes.
- $iprototypeids = [];
- $db_items = DBselect(
- 'SELECT i.itemid'.
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT id.itemid,i.name'.
' FROM item_discovery id,items i'.
- ' WHERE i.itemid=id.itemid'.
- ' AND '.dbConditionInt('parent_itemid', $ruleids)
- );
- while ($item = DBfetch($db_items)) {
- $iprototypeids[$item['itemid']] = $item['itemid'];
- }
- if ($iprototypeids) {
- CItemPrototypeManager::delete($iprototypeids);
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('parent_itemid', $ruleids)
+ ), 'itemid');
+
+ if ($db_items) {
+ CItemPrototype::deleteForce($db_items);
}
// Delete host prototypes.
@@ -64,7 +61,7 @@ class CDiscoveryRuleManager {
'SELECT hd.hostid,h.host'.
' FROM host_discovery hd,hosts h'.
' WHERE hd.hostid=h.hostid'.
- ' AND '.dbConditionInt('hd.parent_itemid', $ruleids)
+ ' AND '.dbConditionId('hd.parent_itemid', $ruleids)
), 'hostid');
if ($db_host_prototypes) {
@@ -81,6 +78,7 @@ class CDiscoveryRuleManager {
DB::delete('items', ['itemid' => $ruleids]);
$insert = [];
+
foreach ($ruleids as $ruleid) {
$insert[] = [
'tablename' => 'events',
@@ -88,6 +86,7 @@ class CDiscoveryRuleManager {
'value' => $ruleid
];
}
+
DB::insertBatch('housekeeper', $insert);
}
}
diff --git a/ui/include/classes/api/managers/CHttpTestManager.php b/ui/include/classes/api/managers/CHttpTestManager.php
index 1376cb737ee..d301b476a54 100644
--- a/ui/include/classes/api/managers/CHttpTestManager.php
+++ b/ui/include/classes/api/managers/CHttpTestManager.php
@@ -46,6 +46,13 @@ class CHttpTestManager {
protected $httpTestParents = [];
/**
+ * Array of parent item IDs indexed by parent httptest ID and item key.
+ *
+ * @var array
+ */
+ private static $parent_itemids = [];
+
+ /**
* Save http test to db.
*
* @param array $httpTests
@@ -96,32 +103,26 @@ class CHttpTestManager {
}
/**
- * Create new http tests.
+ * Create new HTTP tests.
*
- * @param array $httpTests
+ * @param array $httptests
*
* @return array
*/
- public function create(array $httpTests) {
- $httpTestIds = DB::insert('httptest', $httpTests);
+ public function create(array $httptests) {
+ $httptestids = DB::insert('httptest', $httptests);
- foreach ($httpTests as $hnum => &$httpTest) {
- $httpTest['httptestid'] = $httpTestIds[$hnum];
- $itemids = [];
-
- $this->createHttpTestItems($httpTest, $itemids);
- $this->createStepsReal($httpTest, $httpTest['steps'], $itemids);
-
- if (array_key_exists('tags', $httpTest) && $httpTest['tags']) {
- self::createItemsTags($httpTest['tags'], $itemids);
- }
+ foreach ($httptests as &$httptest) {
+ $httptest['httptestid'] = array_shift($httptestids);
}
- unset($httpTest);
+ unset($httptest);
- $this->updateHttpTestFields($httpTests, 'create');
- self::createHttpTestTags($httpTests);
+ self::createItems($httptests);
+ self::updateFields($httptests);
+ self::updateSteps($httptests);
+ self::updateTags($httptests);
- return $httpTests;
+ return $httptests;
}
/**
@@ -132,125 +133,1179 @@ class CHttpTestManager {
* @return array
*/
public function update(array $httptests) {
- $db_httptests = API::HttpTest()->get([
- 'output' => ['httptestid', 'name', 'delay', 'status', 'agent', 'authentication',
- 'http_user', 'http_password', 'hostid', 'templateid', 'http_proxy', 'retries', 'ssl_cert_file',
- 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host'
- ],
- 'selectSteps' => ['httpstepid', 'name', 'no', 'url', 'timeout', 'posts', 'required', 'status_codes',
- 'follow_redirects', 'retrieve_mode'
- ],
- 'httptestids' => array_column($httptests, 'httptestid'),
- 'nopermissions' => true,
- 'preservekeys' => true
- ]);
+ $db_httptests = DBfetchArrayAssoc(DBselect(
+ 'SELECT ht.httptestid,ht.name,ht.delay,ht.retries,ht.agent,ht.http_proxy,ht.status,ht.authentication,'.
+ 'ht.http_user,ht.http_password,ht.verify_peer,ht.verify_host,ht.ssl_cert_file,ht.ssl_key_file,'.
+ 'ht.ssl_key_password,ht.hostid,ht.templateid,h.status AS host_status'.
+ ' FROM httptest ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.httptestid', array_column($httptests, 'httptestid'))
+ ), 'httptestid');
+
+ self::addAffectedObjects($httptests, $db_httptests);
+
+ $upd_httptests = [];
+
+ foreach ($httptests as $httptest) {
+ $upd_httptest = DB::getUpdatedValues('httptest', $httptest, $db_httptests[$httptest['httptestid']]);
+
+ if ($upd_httptest) {
+ $upd_httptests[] = [
+ 'values' => $upd_httptest,
+ 'where' => ['httptestid' => $httptest['httptestid']]
+ ];
+ }
+ }
- $steps_create = [];
- $steps_update = [];
- $del_steps = [];
- $itemids = [];
+ if ($upd_httptests) {
+ DB::update('httptest', $upd_httptests);
+ }
+
+ self::updateItems($httptests, $db_httptests);
+ self::updateFields($httptests, $db_httptests);
+ self::updateSteps($httptests, $db_httptests);
+ self::updateTags($httptests, $db_httptests);
+
+ return $httptests;
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedObjects(array $httptests, array &$db_httptests): void {
+ self::addAffectedItems($httptests, $db_httptests);
+ self::addAffectedFields($httptests, $db_httptests);
+ self::addAffectedSteps($httptests, $db_httptests);
+ self::addAffectedTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedItems(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
- foreach ($httptests as $key => $httptest) {
+ foreach ($httptests as $httptest) {
$db_httptest = $db_httptests[$httptest['httptestid']];
- DB::update('httptest', [
- 'values' => $httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ]);
+ $name_updated = $httptest['name'] != $db_httptest['name'];
+ $status_updated = array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status'];
+ $delay_updated = array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay'];
+ $templateid_updated = array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0;
- if (!array_key_exists($httptest['httptestid'], $itemids)) {
- $itemids[$httptest['httptestid']] = [];
+ if ($name_updated || $status_updated || $delay_updated || $templateid_updated
+ || array_key_exists('tags', $httptest)) {
+ $httptestids[] = $httptest['httptestid'];
}
+ }
- $checkItemsUpdate = [];
- $dbCheckItems = DBselect(
- 'SELECT i.itemid,i.name,i.key_,hi.type'.
- ' FROM items i,httptestitem hi'.
- ' WHERE hi.httptestid='.zbx_dbstr($httptest['httptestid']).
- ' AND hi.itemid=i.itemid'
- );
- while ($checkitem = DBfetch($dbCheckItems)) {
- $itemids[$httptest['httptestid']][] = $checkitem['itemid'];
- $update_fields = [];
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hi.httptestid,hi.itemid,hi.type AS test_type,i.name,i.key_,i.status,i.delay,i.templateid'.
+ ' FROM httptestitem hi,items i'.
+ ' WHERE hi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hi.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['items'][$row['itemid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+
+ self::addAffectedItemTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedItemTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $httptestids[] = $httptest['httptestid'];
+
+ foreach ($db_httptests[$httptest['httptestid']]['items'] as &$db_item) {
+ $db_item['tags'] = [];
+ }
+ unset($db_item);
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hti.httptestid,hti.itemid,it.itemtagid,it.tag,it.value'.
+ ' FROM httptestitem hti,item_tag it'.
+ ' WHERE hti.itemid=it.itemid'.
+ ' AND '.dbConditionId('hti.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['items'][$row['itemid']]['tags'][$row['itemtagid']] =
+ array_diff_key($row, array_flip(['httptestid', 'itemid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedFields(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+ $types = [];
+
+ foreach ($httptests as $httptest) {
+ if (array_key_exists('headers', $httptest)) {
+ $httptestids[$httptest['httptestid']] = true;
+ $types[] = ZBX_HTTPFIELD_HEADER;
+ $db_httptests[$httptest['httptestid']]['headers'] = [];
+ }
+
+ if (array_key_exists('variables', $httptest)) {
+ $httptestids[$httptest['httptestid']] = true;
+ $types[] = ZBX_HTTPFIELD_VARIABLE;
+ $db_httptests[$httptest['httptestid']]['variables'] = [];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['httptest_fieldid', 'httptestid', 'type', 'name', 'value'],
+ 'filter' => [
+ 'httptestid' => array_keys($httptestids),
+ 'type' => $types
+ ]
+ ];
+ $result = DBselect(DB::makeSql('httptest_field', $options));
+
+ while ($row = DBfetch($result)) {
+ $field_name = ($row['type'] == ZBX_HTTPFIELD_HEADER) ? 'headers' : 'variables';
+
+ if (array_key_exists($field_name, $db_httptests[$row['httptestid']])) {
+ $db_httptests[$row['httptestid']][$field_name][$row['httptest_fieldid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedSteps(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $name_updated = $httptest['name'] != $db_httptests[$httptest['httptestid']]['name'];
+
+ if (array_key_exists('steps', $httptest) || $name_updated) {
+ $httptestids[] = $httptest['httptestid'];
+ $db_httptests[$httptest['httptestid']]['steps'] = [];
+ }
+ }
+
+ if ($httptestids) {
+ $options = [
+ 'output' => ['httpstepid', 'httptestid', 'name', 'no', 'url', 'timeout', 'posts', 'required',
+ 'status_codes','follow_redirects', 'retrieve_mode', 'post_type'
+ ],
+ 'filter' => ['httptestid' => $httptestids]
+ ];
+ $result = DBselect(DB::makeSql('httpstep', $options));
- $update_fields['name'] = $this->getTestName($checkitem['type'], $httptest['name']);
- if ($update_fields['name'] === $checkitem['name']) {
- unset($update_fields['name']);
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+
+ self::addAffectedStepItems($httptests, $db_httptests);
+ self::addAffectedStepFields($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepItems(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ $name_updated = $httptest['name'] != $db_httptest['name'];
+ $status_updated = array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status'];
+ $delay_updated = array_key_exists('delay', $httptest) && $httptest['delay'] != $db_httptest['delay'];
+ $templateid_updated = array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0;
+
+ if (array_key_exists('steps', $httptest) || $name_updated || $status_updated || $delay_updated
+ || $templateid_updated || array_key_exists('tags', $httptest)) {
+ $httptestids[] = $httptest['httptestid'];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsi.type AS test_type,hsi.itemid,i.name,i.key_,i.status,i.delay,'.
+ 'i.templateid'.
+ ' FROM httpstep hs,httpstepitem hsi,items i'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']]['items'][$row['itemid']] =
+ array_diff_key($row, array_flip(['httptestid', 'httpstepid']));
+ }
+
+ self::addAffectedStepItemTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepItemTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $httptestids[] = $httptest['httptestid'];
+
+ foreach ($db_httptests[$httptest['httptestid']]['steps'] as &$db_step) {
+ if (!array_key_exists('items', $db_step)) {
+ continue;
+ }
+
+ foreach ($db_step['items'] as &$db_item) {
+ $db_item['tags'] = [];
+ }
+ unset($db_item);
+ }
+ unset($db_step);
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsi.itemid,it.itemtagid,it.tag,it.value'.
+ ' FROM httpstep hs,httpstepitem hsi,item_tag it'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=it.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']]['items'][$row['itemid']]['tags'][$row['itemtagid']] =
+ array_diff_key($row, array_flip(['httptestid', 'httpstepid', 'itemid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepFields(array $httptests, array &$db_httptests): void {
+ $httpstepids = [];
+ $types = [];
+
+ $field_names = [
+ ZBX_HTTPFIELD_HEADER => 'headers',
+ ZBX_HTTPFIELD_VARIABLE => 'variables',
+ ZBX_HTTPFIELD_POST_FIELD => 'posts',
+ ZBX_HTTPFIELD_QUERY_FIELD => 'query_fields'
+ ];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)
+ || !array_key_exists($step['httpstepid'], $db_httptests[$httptest['httptestid']]['steps'])) {
+ continue;
+ }
+
+ $db_step = &$db_httptests[$httptest['httptestid']]['steps'][$step['httpstepid']];
+
+ if (array_key_exists('headers', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_HEADER] = true;
+ $db_step['headers'] = [];
+ }
+
+ if (array_key_exists('variables', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_VARIABLE] = true;
+
+ $db_step['variables'] = [];
+ }
+
+ if (array_key_exists('posts', $step) && $db_step['post_type'] == ZBX_POSTTYPE_FORM) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_POST_FIELD] = true;
+
+ $db_step['posts'] = [];
}
- $update_fields['key_'] = $this->getTestKey($checkitem['type'], $httptest['name']);
- if ($update_fields['key_'] === $checkitem['key_']) {
- unset($update_fields['key_']);
+ if (array_key_exists('query_fields', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_QUERY_FIELD] = true;
+
+ $db_step['query_fields'] = [];
+ }
+ }
+ }
+
+ unset($db_step);
+
+ if (!$httpstepids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsf.httpstep_fieldid,hsf.type,hsf.name,hsf.value'.
+ ' FROM httpstep hs,httpstep_field hsf'.
+ ' WHERE hs.httpstepid=hsf.httpstepid'.
+ ' AND '.dbConditionId('hs.httpstepid', array_keys($httpstepids)).
+ ' AND '.dbConditionInt('hsf.type', array_keys($types))
+ );
+
+ while ($row = DBfetch($result)) {
+ $field_name = $field_names[$row['type']];
+
+ $db_step = &$db_httptests[$row['httptestid']]['steps'][$row['httpstepid']];
+
+ if (array_key_exists($field_name, $db_step)) {
+ $db_step[$field_name][$row['httpstep_fieldid']] = [
+ 'httpstep_fieldid' => $row['httpstep_fieldid'],
+ 'name' => $row['name'],
+ 'value' => $row['value']
+ ];
+ }
+ }
+
+ unset($db_step);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $steps_to_create_exists = array_key_exists('steps', $httptest)
+ && count($httptest['steps']) > count(array_column($httptest['steps'], 'httpstepid'));
+
+ if (array_key_exists('tags', $httptest) || $steps_to_create_exists) {
+ $httptestids[] = $httptest['httptestid'];
+ $db_httptests[$httptest['httptestid']]['tags'] = [];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['httptesttagid', 'httptestid', 'tag', 'value'],
+ 'filter' => ['httptestid' => $httptestids]
+ ];
+ $result = DBselect(DB::makeSql('httptest_tag', $options));
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['tags'][$row['httptesttagid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ */
+ private static function createItems(array $httptests): void {
+ $items = [];
+
+ $type_items = [
+ HTTPSTEP_ITEM_TYPE_IN => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 'Bps'
+ ],
+ HTTPSTEP_ITEM_TYPE_LASTSTEP => [
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => ''
+ ],
+ HTTPSTEP_ITEM_TYPE_LASTERROR => [
+ 'value_type' => ITEM_VALUE_TYPE_STR,
+ 'units' => ''
+ ]
+ ];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('status', $httptest)) {
+ $httptest['status'] = DB::getDefault('httptest', 'status');
+ }
+
+ $item_status = $httptest['status'] == HTTPTEST_STATUS_ACTIVE ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
+ $item_tags = array_key_exists('tags', $httptest) ? $httptest['tags'] : [];
+
+ if (!array_key_exists('delay', $httptest)) {
+ $httptest['delay'] = DB::getDefault('httptest', 'delay');
+ }
+
+ foreach ($type_items as $type => $type_item) {
+ $item_key = self::getTestKey($type, $httptest['name']);
+
+ $items[] = [
+ 'host_status' => $httptest['host_status'],
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hostid' => $httptest['hostid'],
+ 'name' => self::getTestName($type, $httptest['name']),
+ 'type' => ITEM_TYPE_HTTPTEST,
+ 'key_' => $item_key,
+ 'history' => self::ITEM_HISTORY,
+ 'trends' => self::ITEM_TRENDS,
+ 'status' => $item_status,
+ 'tags' => $item_tags,
+ 'delay' => $httptest['delay'],
+ 'templateid' => array_key_exists('templateid', $httptest)
+ ? self::$parent_itemids[$httptest['templateid']][$item_key]
+ : 0
+ ] + $type_item;
+ }
+ }
+
+ CItem::createForce($items);
+
+ $itemids = array_column($items, 'itemid');
+
+ $ins_httptestitems = [];
+
+ foreach ($httptests as $httptest) {
+ foreach ($type_items as $type => $foo) {
+ $ins_httptestitems[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'itemid' => array_shift($itemids),
+ 'type' => $type
+ ];
+ }
+ }
+
+ DB::insertBatch('httptestitem', $ins_httptestitems);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function updateItems(array $httptests, array $db_httptests): void {
+ $items = [];
+ $db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('items', $db_httptests[$httptest['httptestid']])) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ $db_items += $db_httptest['items'];
+
+ foreach ($db_httptest['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name']) {
+ $item += [
+ 'name' => self::getTestName($db_item['test_type'], $httptest['name']),
+ 'key_' => self::getTestKey($db_item['test_type'], $httptest['name'])
+ ];
}
- if (isset($httptest['status'])) {
- $update_fields['status'] = (HTTPTEST_STATUS_ACTIVE == $httptest['status'])
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
? ITEM_STATUS_ACTIVE
: ITEM_STATUS_DISABLED;
}
- if (isset($httptest['delay'])) {
- $update_fields['delay'] = $httptest['delay'];
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
}
- if (!empty($update_fields)) {
- $checkItemsUpdate[] = [
- 'values' => $update_fields,
- 'where' => ['itemid' => $checkitem['itemid']]
- ];
+
+ if (array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0) {
+ $item_key = array_key_exists('key_', $item) ? $item['key_'] : $db_item['key_'];
+
+ $item['templateid'] = $httptest['templateid'] == 0
+ ? 0
+ : self::$parent_itemids[$httptest['templateid']][$item_key];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
}
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
}
- DB::update('items', $checkItemsUpdate);
if (array_key_exists('steps', $httptest)) {
- $dbSteps = zbx_toHash($db_httptest['steps'], 'httpstepid');
+ continue;
+ }
+
+ foreach ($db_httptest['steps'] as $db_step) {
+ $db_items += $db_step['items'];
+
+ foreach ($db_step['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name']) {
+ $item += [
+ 'name' => self::getStepName($db_item['test_type'], $httptest['name'], $db_step['name']),
+ 'key_' => self::getStepKey($db_item['test_type'], $httptest['name'], $db_step['name'])
+ ];
+ }
+
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
+ ? ITEM_STATUS_ACTIVE
+ : ITEM_STATUS_DISABLED;
+ }
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
+ }
+
+ if (array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0) {
+ $item_key = array_key_exists('key_', $item) ? $item['key_'] : $db_item['key_'];
+
+ $item['templateid'] = $httptest['templateid'] == 0
+ ? 0
+ : self::$parent_itemids[$httptest['templateid']][$item_key];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
+ }
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
+ }
+ }
+ }
+
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateFields(array &$httptests, array $db_httptests = null): void {
+ $ins_fields = [];
+ $upd_fields = [];
+ $del_fieldids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (array_key_exists('headers', $httptest)) {
+ $db_headers = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['headers'] : [];
+
+ foreach ($httptest['headers'] as &$header) {
+ $db_fieldid = key(array_filter($db_headers,
+ static function (array $db_header) use ($header): bool {
+ return $header['name'] == $db_header['name'] && $header['value'] == $db_header['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $header['httptest_fieldid'] = $db_fieldid;
+ unset($db_headers[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'type' => ZBX_HTTPFIELD_HEADER
+ ] + $header;
+ }
+ }
+ unset($header);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_headers));
+ }
+
+ if (array_key_exists('variables', $httptest)) {
+ $db_variables = $db_httptests !== null
+ ? array_column($db_httptests[$httptest['httptestid']]['variables'], null, 'name')
+ : [];
+
+ foreach ($httptest['variables'] as &$variable) {
+ if (array_key_exists($variable['name'], $db_variables)) {
+ $db_variable = $db_variables[$variable['name']];
+
+ $upd_variable = DB::getUpdatedValues('httptest_field', $variable, $db_variable);
+
+ if ($upd_variable) {
+ $upd_fields[] = [
+ 'values' => $upd_variable,
+ 'where' => ['httptest_fieldid' => $db_variable['httptest_fieldid']]
+ ];
+ }
- foreach ($httptest['steps'] as $webstep) {
- if (isset($webstep['httpstepid']) && isset($dbSteps[$webstep['httpstepid']])) {
- $steps_update[$key][] = $webstep;
- unset($dbSteps[$webstep['httpstepid']]);
+ $variable['httptest_fieldid'] = $db_variable['httptest_fieldid'];
+ unset($db_variables[$variable['name']]);
}
- elseif (!isset($webstep['httpstepid'])) {
- $steps_create[$key][] = $webstep;
+ else {
+ $ins_fields[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'type' => ZBX_HTTPFIELD_VARIABLE
+ ] + $variable;
}
}
+ unset($variable);
- $del_steps += $dbSteps;
+ $del_fieldids = array_merge($del_fieldids, array_column($db_variables, 'httptest_fieldid'));
}
}
+ unset($httptest);
+
+ if ($del_fieldids) {
+ DB::delete('httptest_field', ['httptest_fieldid' => $del_fieldids]);
+ }
+
+ if ($upd_fields) {
+ DB::update('httptest_field', $upd_fields);
+ }
+
+ if ($ins_fields) {
+ DB::insertBatch('httptest_field', $ins_fields);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateSteps(array &$httptests, array $db_httptests = null): void {
+ $ins_steps = [];
+ $upd_steps = [];
+ $update_step_items = false;
+ $del_stepids = [];
+ $del_db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_steps = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['steps'] : [];
+
+ foreach ($httptest['steps'] as $step) {
+ if (array_key_exists('httpstepid', $step)) {
+ if (array_key_exists('posts', $step)) {
+ if (is_array($step['posts'])) {
+ if ($db_steps[$step['httpstepid']]['post_type'] == ZBX_POSTTYPE_RAW) {
+ if ($step['posts']) {
+ $step['post_type'] = ZBX_POSTTYPE_FORM;
+ }
+
+ $step['posts'] = '';
+ }
+ else {
+ if (!$step['posts']) {
+ $step['post_type'] = ZBX_POSTTYPE_RAW;
+ }
+
+ unset($step['posts']);
+ }
+ }
+ else {
+ if ($db_steps[$step['httpstepid']]['post_type'] == ZBX_POSTTYPE_FORM) {
+ $step['post_type'] = ZBX_POSTTYPE_RAW;
+ $db_steps[$step['httpstepid']]['posts'] = '';
+ }
+ }
+ }
+
+ $upd_step = DB::getUpdatedValues('httpstep', $step, $db_steps[$step['httpstepid']]);
+
+ if ($upd_step) {
+ $upd_steps[] = [
+ 'values' => $upd_step,
+ 'where' => ['httpstepid' => $step['httpstepid']]
+ ];
+ }
+
+ if (array_key_exists('items', $db_steps[$step['httpstepid']])) {
+ $update_step_items = true;
+ }
+
+ unset($db_steps[$step['httpstepid']]);
+ }
+ else {
+ if (array_key_exists('posts', $step) && is_array($step['posts'])) {
+ $step['post_type'] = ZBX_POSTTYPE_FORM;
+ unset($step['posts']);
+ }
- // Old items must be deleted prior to createStepsReal() since identical items cannot be created in DB.
- if ($del_steps) {
- $del_stepids = array_keys($del_steps);
+ $ins_steps[] = ['httptestid' => $httptest['httptestid']] + $step;
+ }
+ }
- CHttpTest::deleteAffectedStepItems($del_stepids);
+ $del_stepids = array_merge($del_stepids, array_keys($db_steps));
+
+ foreach ($db_steps as $db_step) {
+ if (!array_key_exists('items', $db_step)) {
+ continue;
+ }
+
+ foreach ($db_step['items'] as $db_item) {
+ $del_db_items[$db_item['itemid']] = $db_item;
+ }
+ }
+ }
+
+ if ($del_stepids) {
+ if ($del_db_items) {
+ CItem::addInheritedItems($del_db_items);
+ DB::delete('httpstepitem', ['itemid' => array_keys($del_db_items)]);
+ CItem::deleteForce($del_db_items);
+ }
DB::delete('httpstep_field', ['httpstepid' => $del_stepids]);
DB::delete('httpstep', ['httpstepid' => $del_stepids]);
}
- foreach ($httptests as $key => $httptest) {
- if (array_key_exists('steps', $httptest)) {
- if (array_key_exists($key, $steps_update)) {
- $this->updateStepsReal($httptest, $steps_update[$key], $itemids[$httptest['httptestid']]);
+ if ($upd_steps) {
+ DB::update('httpstep', $upd_steps);
+ }
+
+ if ($update_step_items) {
+ self::updateStepItems($httptests, $db_httptests);
+ }
+
+ if ($ins_steps) {
+ $stepids = DB::insert('httpstep', $ins_steps);
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
}
- if (array_key_exists($key, $steps_create)) {
- $this->createStepsReal($httptest, $steps_create[$key], $itemids[$httptest['httptestid']]);
+ foreach ($httptest['steps'] as &$step) {
+ if (!array_key_exists('httpstepid', $step)) {
+ $step['httpstepid'] = current($stepids);
+ next($stepids);
+ }
}
+ unset($step);
}
- else {
- $this->updateStepItems($httptest, $db_httptests[$httptest['httptestid']]);
+ unset($httptest);
+
+ self::createStepItems($httptests, $stepids, $db_httptests);
+ }
+
+ self::updateStepFields($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function updateStepItems(array $httptests, array $db_httptests): void {
+ $items = [];
+ $db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)
+ || !array_key_exists('items', $db_httptest['steps'][$step['httpstepid']])) {
+ continue;
+ }
+
+ $db_step = $db_httptest['steps'][$step['httpstepid']];
+
+ if (!array_key_exists('name', $step)) {
+ $step['name'] = $db_step['name'];
+ }
+
+ $db_items += $db_step['items'];
+
+ foreach ($db_step['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name'] || $step['name'] !== $db_step['name']) {
+ $item += [
+ 'name' => self::getStepName($db_item['test_type'], $httptest['name'], $step['name']),
+ 'key_' => self::getStepKey($db_item['test_type'], $httptest['name'], $step['name'])
+ ];
+ }
+
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
+ ? ITEM_STATUS_ACTIVE
+ : ITEM_STATUS_DISABLED;
+ }
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
+ }
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
+ }
}
}
- $this->updateHttpTestFields($httptests, 'update');
- self::updateHttpTestTags($httptests);
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $stepids
+ * @param array|null $db_httptests
+ */
+ private static function createStepItems(array $httptests, array $stepids, ?array $db_httptests): void {
+ $items = [];
+
+ $type_items = [
+ HTTPSTEP_ITEM_TYPE_RSPCODE => [
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => ''
+ ],
+ HTTPSTEP_ITEM_TYPE_TIME => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 's'
+ ],
+ HTTPSTEP_ITEM_TYPE_IN => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 'Bps'
+ ]
+ ];
foreach ($httptests as $httptest) {
- $tags = array_key_exists('tags', $httptest) ? $httptest['tags'] : [];
- self::updateItemsTags($tags, $itemids[$httptest['httptestid']]);
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ if ($db_httptests !== null) {
+ $httptest['host_status'] = $db_httptests[$httptest['httptestid']]['host_status'];
+ }
+
+ if (!array_key_exists('status', $httptest)) {
+ $httptest['status'] = ($db_httptests !== null)
+ ? $db_httptests[$httptest['httptestid']]['status']
+ : DB::getDefault('httptest', 'status');
+ }
+
+ $item_status = $httptest['status'] == HTTPTEST_STATUS_ACTIVE ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
+
+ $item_tags = [];
+
+ if (array_key_exists('tags', $httptest)) {
+ $item_tags = $httptest['tags'];
+ }
+ elseif ($db_httptests !== null) {
+ foreach ($db_httptests[$httptest['httptestid']]['tags'] as $tag) {
+ $item_tags[] = array_intersect_key($tag, array_flip(['tag', 'value']));
+ }
+ }
+
+ if (!array_key_exists('delay', $httptest)) {
+ $httptest['delay'] = ($db_httptests !== null)
+ ? $db_httptests[$httptest['httptestid']]['delay']
+ : DB::getDefault('httptest', 'delay');
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!in_array($step['httpstepid'], $stepids)) {
+ continue;
+ }
+
+ foreach ($type_items as $type => $type_item) {
+ $item_key = self::getStepKey($type, $httptest['name'], $step['name']);
+
+ $items[] = [
+ 'host_status' => $httptest['host_status'],
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hostid' => $httptest['hostid'],
+ 'name' => self::getStepName($type, $httptest['name'], $step['name']),
+ 'type' => ITEM_TYPE_HTTPTEST,
+ 'key_' => $item_key,
+ 'history' => self::ITEM_HISTORY,
+ 'trends' => self::ITEM_TRENDS,
+ 'status' => $item_status,
+ 'tags' => $item_tags,
+ 'delay' => $httptest['delay'],
+ 'templateid' => array_key_exists('templateid', $httptest)
+ ? self::$parent_itemids[$httptest['templateid']][$item_key]
+ : 0
+ ] + $type_item;
+ }
+ }
}
- return $httptests;
+ CItem::createForce($items);
+
+ $itemids = array_column($items, 'itemid');
+
+ $ins_httpstepitems = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!in_array($step['httpstepid'], $stepids)) {
+ continue;
+ }
+
+ foreach ($type_items as $type => $foo) {
+ $ins_httpstepitems[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'itemid' => array_shift($itemids),
+ 'type' => $type
+ ];
+ }
+ }
+ }
+
+ DB::insertBatch('httpstepitem', $ins_httpstepitems);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateStepFields(array &$httptests, ?array $db_httptests): void {
+ $ins_fields = [];
+ $upd_fields = [];
+ $del_fieldids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests !== null ? $db_httptests[$httptest['httptestid']] : null;
+
+ foreach ($httptest['steps'] as &$step) {
+ $db_step = ($db_httptest !== null && array_key_exists($step['httpstepid'], $db_httptest['steps']))
+ ? $db_httptest['steps'][$step['httpstepid']]
+ : null;
+
+ if (array_key_exists('headers', $step)) {
+ $db_headers = $db_step !== null ? $db_step['headers'] : [];
+
+ foreach ($step['headers'] as &$header) {
+ $db_fieldid = key(array_filter($db_headers,
+ static function (array $db_header) use ($header): bool {
+ return $header['name'] == $db_header['name'] && $header['value'] == $db_header['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $header['httpstep_fieldid'] = $db_fieldid;
+ unset($db_headers[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_HEADER
+ ] + $header;
+ }
+ }
+ unset($header);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_headers));
+ }
+
+ if (array_key_exists('variables', $step)) {
+ $db_variables = $db_step !== null ? array_column($db_step['variables'], null, 'name') : [];
+
+ foreach ($step['variables'] as &$variable) {
+ if (array_key_exists($variable['name'], $db_variables)) {
+ $db_variable = $db_variables[$variable['name']];
+
+ $upd_variable = DB::getUpdatedValues('httpstep_field', $variable, $db_variable);
+
+ if ($upd_variable) {
+ $upd_fields[] = [
+ 'values' => $upd_variable,
+ 'where' => ['httpstep_fieldid' => $db_variable['httpstep_fieldid']]
+ ];
+ }
+
+ $variable['httpstep_fieldid'] = $db_variable['httpstep_fieldid'];
+ unset($db_variables[$variable['name']]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_VARIABLE
+ ] + $variable;
+ }
+ }
+ unset($variable);
+
+ $del_fieldids = array_merge($del_fieldids, array_column($db_variables, 'httpstep_fieldid'));
+ }
+
+ if (array_key_exists('posts', $step)) {
+ if (is_array($step['posts'])) {
+ $db_posts = $db_step !== null && is_array($db_step['posts']) ? $db_step['posts'] : [];
+
+ foreach ($step['posts'] as &$post) {
+ $db_fieldid = key(array_filter($db_posts,
+ static function (array $db_post) use ($post): bool {
+ return $post['name'] == $db_post['name'] && $post['value'] == $db_post['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $post['httpstep_fieldid'] = $db_fieldid;
+ unset($db_posts[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_POST_FIELD
+ ] + $post;
+ }
+ }
+ unset($post);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_posts));
+ }
+ elseif ($db_step !== null && is_array($db_step['posts'])) {
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_step['posts']));
+ }
+ }
+
+ if (array_key_exists('query_fields', $step)) {
+ $db_query_fields = $db_step !== null ? $db_step['query_fields'] : [];
+
+ foreach ($step['query_fields'] as &$query_field) {
+ $db_fieldid = key(array_filter($db_query_fields,
+ static function (array $db_query_field) use ($query_field): bool {
+ return $query_field['name'] == $db_query_field['name']
+ && $query_field['value'] == $db_query_field['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $query_field['httpstep_fieldid'] = $db_fieldid;
+ unset($db_query_fields[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_QUERY_FIELD
+ ] + $query_field;
+ }
+ }
+ unset($query_field);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_query_fields));
+ }
+ }
+ unset($step);
+ }
+ unset($httptest);
+
+ if ($del_fieldids) {
+ DB::delete('httpstep_field', ['httpstep_fieldid' => $del_fieldids]);
+ }
+
+ if ($upd_fields) {
+ DB::update('httpstep_field', $upd_fields);
+ }
+
+ if ($ins_fields) {
+ DB::insertBatch('httpstep_field', $ins_fields);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateTags(array &$httptests, array $db_httptests = null): void {
+ $ins_tags = [];
+ $del_tagids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $db_tags = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['tags'] : [];
+
+ foreach ($httptest['tags'] as &$tag) {
+ $db_tagid = key(array_filter($db_tags, static function (array $db_tag) use ($tag): bool {
+ return $tag['tag'] == $db_tag['tag']
+ && (!array_key_exists('value', $tag) || $tag['value'] == $db_tag['value']);
+ }));
+
+ if ($db_tagid !== null) {
+ $tag['httptesttagid'] = $db_tagid;
+ unset($db_tags[$db_tagid]);
+ }
+ else {
+ $ins_tags[] = ['httptestid' => $httptest['httptestid']] + $tag;
+ }
+ }
+ unset($tag);
+
+ $del_tagids = array_merge($del_tagids, array_keys($db_tags));
+ }
+ unset($httptest);
+
+ if ($del_tagids) {
+ DB::delete('httptest_tag', ['httptesttagid' => $del_tagids]);
+ }
+
+ if ($ins_tags) {
+ DB::insert('httptest_tag', $ins_tags);
+ }
}
/**
@@ -281,21 +1336,29 @@ class CHttpTestManager {
/**
* Inherit passed http tests to hosts.
- * If $hostIds is empty that means that we need to inherit all $httpTests to hosts which are linked to templates
- * where $httpTests belong.
- * *
- * @param array $httpTests
+ * If $hostids is empty that means that we need to inherit all $httptests to hosts which are linked to templates
+ * where $httptests belong.
+ *
+ * @param array $httptests
* @param array $hostIds
*
* @return bool
*/
- public function inherit(array $httpTests, array $hostIds = []) {
- $hostsTemplatesMap = $this->getChildHostsFromHttpTests($httpTests, $hostIds);
- if (empty($hostsTemplatesMap)) {
+ public function inherit(array $httptests, array $hostids = []) {
+ $template_hosts = $this->getTemplateHosts($httptests, $hostids);
+
+ if (!$template_hosts) {
return true;
}
- $preparedHttpTests = $this->prepareInheritedHttpTests($httpTests, $hostsTemplatesMap);
+ foreach ($httptests as $i => $httptest) {
+ if (!array_key_exists($httptest['hostid'], $template_hosts)) {
+ unset($httptests[$i]);
+ }
+ }
+
+ self::$parent_itemids = self::getItemIds($httptests);
+ $preparedHttpTests = $this->prepareInheritedHttpTests($httptests, $template_hosts);
$inheritedHttpTests = $this->save($preparedHttpTests);
$this->inherit($inheritedHttpTests);
@@ -303,30 +1366,69 @@ class CHttpTestManager {
}
/**
- * Get array with hosts that are linked with templates which passed http tests belong to as key and templateid that host
- * is linked to as value.
- * If second parameter $hostIds is not empty, result should contain only passed host ids.
+ * Get hosts to which is necessary to inherit the given web scenarios indexed by template ID.
*
- * @param array $httpTests
- * @param array $hostIds
+ * @param array $httptests
+ * @param array $hostids
*
* @return array
*/
- protected function getChildHostsFromHttpTests(array $httpTests, array $hostIds = []) {
- $hostsTemplatesMap = [];
+ private static function getTemplateHosts(array $httptests, array $hostids): array {
+ $template_hosts = [];
- $sqlWhere = $hostIds ? ' AND '.dbConditionInt('ht.hostid', $hostIds) : '';
- $dbCursor = DBselect(
- 'SELECT ht.templateid,ht.hostid'.
- ' FROM hosts_templates ht'.
- ' WHERE '.dbConditionInt('ht.templateid', zbx_objectValues($httpTests, 'hostid')).
- $sqlWhere
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ht.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ht.templateid,ht.hostid,h.status'.
+ ' FROM hosts_templates ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_column($httptests, 'hostid')).
+ $hostids_condition
+ );
+
+ while ($row = DBfetch($result)) {
+ $template_hosts[$row['templateid']][$row['hostid']] = array_diff_key($row, array_flip(['templateid']));
+ }
+
+ return $template_hosts;
+ }
+
+ /**
+ * Get item IDs array of the given web scenarios and their steps indexed by httptest ID and item key.
+ *
+ * @param array $httptests
+ *
+ * @return array
+ */
+ private static function getItemIds(array $httptests): array {
+ $httptest_itemids = [];
+
+ $httptestids = array_column($httptests, 'httptestid');
+
+ $result = DBselect(
+ 'SELECT hti.httptestid,hti.itemid,i.key_'.
+ ' FROM httptestitem hti,items i'.
+ ' WHERE hti.itemid=i.itemid'.
+ ' AND '.dbConditionId('hti.httptestid', $httptestids)
);
- while ($dbHost = DBfetch($dbCursor)) {
- $hostsTemplatesMap[$dbHost['hostid']] = $dbHost['templateid'];
+
+ while ($row = DBfetch($result)) {
+ $httptest_itemids[$row['httptestid']][$row['key_']] = $row['itemid'];
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hsi.itemid,i.key_'.
+ ' FROM httpstep hs,httpstepitem hsi,items i'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $httptest_itemids[$row['httptestid']][$row['key_']] = $row['itemid'];
}
- return $hostsTemplatesMap;
+ return $httptest_itemids;
}
/**
@@ -334,20 +1436,20 @@ class CHttpTestManager {
* Using passed parameters decide if new http tests must be created on host or existing ones must be updated.
*
* @param array $httpTests which we need to inherit
- * @param array $hostsTemplatesMap
+ * @param array $template_hosts
*
* @throws Exception
* @return array with http tests, existing apps have 'httptestid' key.
*/
- protected function prepareInheritedHttpTests(array $httpTests, array $hostsTemplatesMap) {
- $hostHttpTests = $this->getHttpTestsMapsByHostIds(array_keys($hostsTemplatesMap));
+ protected function prepareInheritedHttpTests(array $httpTests, array $template_hosts) {
+ $hostHttpTests = $this->getHostHttpTests($template_hosts);
$result = [];
foreach ($httpTests as $httpTest) {
$httpTestId = $httpTest['httptestid'];
foreach ($hostHttpTests as $hostId => $hostHttpTest) {
// if http test template is not linked to host we skip it
- if ($hostsTemplatesMap[$hostId] != $httpTest['hostid']) {
+ if (!array_key_exists($hostId, $template_hosts[$httpTest['hostid']])) {
continue;
}
@@ -380,10 +1482,6 @@ class CHttpTestManager {
_s('Web scenario "%1$s" already exists on host "%2$s".', $exHttpTest['name'], $host['name'])
);
}
- elseif ($this->compareHttpProperties($httpTest, $exHttpTest)) {
- $this->createLinkageBetweenHttpTests($httpTestId, $exHttpTest['httptestid']);
- continue;
- }
}
$newHttpTest = $httpTest;
@@ -393,14 +1491,34 @@ class CHttpTestManager {
if ($exHttpTest) {
$newHttpTest['httptestid'] = $exHttpTest['httptestid'];
- $this->setHttpTestParent($exHttpTest['httptestid'], $httpTestId);
+ if (array_key_exists('variables', $newHttpTest)) {
+ foreach ($newHttpTest['variables'] as &$variable) {
+ unset($variable['httptest_fieldid']);
+ }
+ unset($variable);
+ }
- if (isset($newHttpTest['steps'])) {
- $newHttpTest['steps'] = $this->prepareHttpSteps($httpTest['steps'], $exHttpTest['httptestid']);
+ if (isset($hostHttpTest['byTemplateId'][$httpTestId])) {
+ $this->setHttpTestParent($exHttpTest['httptestid'], $httpTestId);
+
+ if (isset($newHttpTest['steps'])) {
+ $newHttpTest['steps'] = $this->prepareHttpSteps($httpTest['steps'],
+ $exHttpTest['httptestid']
+ );
+ }
+ }
+ elseif (isset($hostHttpTest['byName'][$httpTest['name']])) {
+ unset($newHttpTest['steps']);
}
}
else {
unset($newHttpTest['httptestid']);
+ $newHttpTest['host_status'] = $template_hosts[$httpTest['hostid']][$hostId]['status'];
+
+ foreach ($newHttpTest['steps'] as &$step) {
+ unset($step['httpstepid']);
+ }
+ unset($step);
}
$result[] = $newHttpTest;
@@ -411,72 +1529,6 @@ class CHttpTestManager {
}
/**
- * Compare properties for http tests.
- *
- * @param array $http_test Current http test properties.
- * @param array $ex_http_test Existing http test properties to compare with.
- *
- * @return bool
- */
- protected function compareHttpProperties(array $http_test, array $ex_http_test) {
- return ($http_test['http_proxy'] === $ex_http_test['http_proxy']
- && $http_test['agent'] === $ex_http_test['agent']
- && $http_test['retries'] == $ex_http_test['retries']
- && $http_test['delay'] === $ex_http_test['delay']);
- }
-
- /**
- * Create linkage between two http tests.
- * If we found existing http test by name and steps, we only add linkage, i.e. change templateid
- *
- * @param $parentId
- * @param $childId
- */
- protected function createLinkageBetweenHttpTests($parentId, $childId) {
- DB::update('httptest', [
- 'values' => [
- 'templateid' => $parentId,
- 'uuid' => ''
- ],
- 'where' => ['httptestid' => $childId]
- ]);
-
- $dbCursor = DBselect(
- 'SELECT i1.itemid AS parentid,i2.itemid AS childid'.
- ' FROM httptestitem hti1,httptestitem hti2,items i1,items i2'.
- ' WHERE hti1.httptestid='.zbx_dbstr($parentId).
- ' AND hti2.httptestid='.zbx_dbstr($childId).
- ' AND hti1.itemid=i1.itemid'.
- ' AND hti2.itemid=i2.itemid'.
- ' AND i1.key_=i2.key_'
- );
- while ($dbItems = DBfetch($dbCursor)) {
- DB::update('items', [
- 'values' => ['templateid' => $dbItems['parentid']],
- 'where' => ['itemid' => $dbItems['childid']]
- ]);
- }
-
- $dbCursor = DBselect(
- 'SELECT i1.itemid AS parentid,i2.itemid AS childid'.
- ' FROM httpstepitem hsi1,httpstepitem hsi2,httpstep hs1,httpstep hs2,items i1,items i2'.
- ' WHERE hs1.httptestid='.zbx_dbstr($parentId).
- ' AND hs2.httptestid='.zbx_dbstr($childId).
- ' AND hsi1.itemid=i1.itemid'.
- ' AND hsi2.itemid=i2.itemid'.
- ' AND hs1.httpstepid=hsi1.httpstepid'.
- ' AND hs2.httpstepid=hsi2.httpstepid'.
- ' AND i1.key_=i2.key_'
- );
- while ($dbItems = DBfetch($dbCursor)) {
- DB::update('items', [
- 'values' => ['templateid' => $dbItems['parentid']],
- 'where' => ['itemid' => $dbItems['childid']]
- ]);
- }
- }
-
- /**
* Find and set first parent id for http test.
*
* @param $id
@@ -501,20 +1553,23 @@ class CHttpTestManager {
* ), ...
* );
*
- * @param array $hostIds
+ * @param array $template_hosts
*
* @return array
*/
- protected function getHttpTestsMapsByHostIds(array $hostIds) {
+ protected function getHostHttpTests(array $template_hosts) {
$hostHttpTests = [];
- foreach ($hostIds as $hostid) {
- $hostHttpTests[$hostid] = ['byName' => [], 'byTemplateId' => []];
+
+ foreach ($template_hosts as $hosts) {
+ foreach ($hosts as $hostid => $foo) {
+ $hostHttpTests[$hostid] = ['byName' => [], 'byTemplateId' => []];
+ }
}
$dbCursor = DBselect(
'SELECT ht.httptestid,ht.name,ht.delay,ht.agent,ht.hostid,ht.templateid,ht.http_proxy,ht.retries'.
' FROM httptest ht'.
- ' WHERE '.dbConditionInt('ht.hostid', $hostIds)
+ ' WHERE '.dbConditionId('ht.hostid', array_keys($hostHttpTests))
);
while ($dbHttpTest = DBfetch($dbCursor)) {
$hostHttpTests[$dbHttpTest['hostid']]['byName'][$dbHttpTest['name']] = $dbHttpTest;
@@ -632,638 +1687,22 @@ class CHttpTestManager {
if (isset($exSteps[$stepName])) {
$step['httpstepid'] = $exSteps[$stepName];
- $step['httptestid'] = $exHttpTestId;
- }
-
- $result[] = $step;
- }
-
- return $result;
- }
-
- /**
- * Create items required for web scenario.
- *
- * @param array $http_test
- * @param array $_itemids
- *
- * @throws Exception
- */
- protected function createHttpTestItems(array $http_test, array &$_itemids): void {
- $checkitems = [
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_IN, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_IN, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 'Bps',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_IN
- ],
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_LASTSTEP, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_LASTSTEP, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'units' => '',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_LASTSTEP
- ],
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_LASTERROR, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_LASTERROR, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_STR,
- 'units' => '',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_LASTERROR
- ]
- ];
-
- // if this is a template scenario, fetch the parent http items to link inherited items to them
- $parent_items = [];
- if (isset($http_test['templateid']) && $http_test['templateid']) {
- $parent_items = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid,i.key_'.
- ' FROM items i,httptestitem hti'.
- ' WHERE i.itemid=hti.itemid'.
- ' AND hti.httptestid='.zbx_dbstr($http_test['templateid'])
- ), 'key_');
- }
-
- $delay = array_key_exists('delay', $http_test) ? $http_test['delay'] : DB::getDefault('httptest', 'delay');
- $status = array_key_exists('status', $http_test) ? $http_test['status'] : DB::getDefault('httptest', 'status');
-
- $ins_items = [];
- foreach ($checkitems as $item) {
- $item['hostid'] = $http_test['hostid'];
- $item['delay'] = $delay;
- $item['type'] = ITEM_TYPE_HTTPTEST;
- $item['history'] = self::ITEM_HISTORY;
- $item['trends'] = self::ITEM_TRENDS;
- $item['status'] = ($status == HTTPTEST_STATUS_ACTIVE)
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
-
- if (isset($parent_items[$item['key_']])) {
- $item['templateid'] = $parent_items[$item['key_']]['itemid'];
- }
-
- $ins_items[] = $item;
- }
- $itemids = DB::insert('items', $ins_items);
-
- $ins_item_rtdata = [];
- foreach ($itemids as $itemid) {
- $_itemids[] = $itemid;
- $ins_item_rtdata[] = ['itemid' => $itemid];
- }
- DB::insertBatch('item_rtdata', $ins_item_rtdata, false);
-
- $ins_httptestitem = [];
- foreach ($checkitems as $inum => $item) {
- $ins_httptestitem[] = [
- 'httptestid' => $http_test['httptestid'],
- 'itemid' => $itemids[$inum],
- 'type' => $item['httptestitemtype']
- ];
- }
- DB::insertBatch('httptestitem', $ins_httptestitem);
- }
-
- /**
- * Create web scenario fields.
- *
- * @param array $httptests
- * @param string $httptests['httptestid']
- * @param array $httptests['variables'] (optional)
- * @param string $httptests['variables']['name']
- * @param string $httptests['variables']['value']
- * @param array $httptests['headers'] (optional)
- * @param string $httptests['headers']['name']
- * @param string $httptests['headers']['value']
- * @param string $method
- */
- private function updateHttpTestFields(array $httptests, $method) {
- $fields = [
- ZBX_HTTPFIELD_VARIABLE => 'variables',
- ZBX_HTTPFIELD_HEADER => 'headers'
- ];
- $httptest_fields = [];
-
- foreach ($httptests as $httptest) {
- foreach ($fields as $type => $field) {
- if (array_key_exists($field, $httptest)) {
- $httptest_fields[$httptest['httptestid']][$type] = $httptest[$field];
- }
- }
- }
-
- if (!$httptest_fields) {
- return;
- }
-
- $db_httptest_fields = ($method === 'update')
- ? DB::select('httptest_field', [
- 'output' => ['httptest_fieldid', 'httptestid', 'type', 'name', 'value'],
- 'filter' => ['httptestid' => array_keys($httptest_fields)],
- 'sortfield' => ['httptest_fieldid']
- ])
- : [];
-
- $ins_httptest_fields = [];
- $upd_httptest_fields = [];
- $del_httptest_fieldids = [];
-
- foreach ($db_httptest_fields as $index => $db_httptest_field) {
- if (array_key_exists($db_httptest_field['type'], $httptest_fields[$db_httptest_field['httptestid']])) {
- $httptest_field =
- array_shift($httptest_fields[$db_httptest_field['httptestid']][$db_httptest_field['type']]);
-
- if ($httptest_field !== null) {
- $upd_httptest_field = [];
-
- foreach (['name', 'value'] as $field_name) {
- if ($httptest_field[$field_name] !== $db_httptest_field[$field_name]) {
- $upd_httptest_field[$field_name] = $httptest_field[$field_name];
- }
- }
-
- if ($upd_httptest_field) {
- $upd_httptest_fields[] = [
- 'values' => $upd_httptest_field,
- 'where' => ['httptest_fieldid' => $db_httptest_field['httptest_fieldid']]
- ];
- }
- }
- else {
- $del_httptest_fieldids[] = $db_httptest_field['httptest_fieldid'];
- }
- }
- }
-
- foreach ($httptest_fields as $httptestid => $httptest_fields_by_httptest) {
- foreach ($httptest_fields_by_httptest as $type => $httptest_fields_by_type) {
- foreach ($httptest_fields_by_type as $httptest_field) {
- $ins_httptest_fields[] = [
- 'httptestid' => $httptestid,
- 'type' => $type
- ] + $httptest_field;
- }
- }
- }
-
- if ($ins_httptest_fields) {
- DB::insertBatch('httptest_field', $ins_httptest_fields);
- }
-
- if ($upd_httptest_fields) {
- DB::update('httptest_field', $upd_httptest_fields);
- }
-
- if ($del_httptest_fieldids) {
- DB::delete('httptest_field', ['httptest_fieldid' => $del_httptest_fieldids]);
- }
- }
-
- /**
- * Create web scenario step fields.
- *
- * @param array $httpsteps
- * @param string $httpsteps['httpstepid']
- * @param array $httpsteps['variables'] (optional)
- * @param string $httpsteps['variables']['name']
- * @param string $httpsteps['variables']['value']
- * @param array $httpsteps['headers'] (optional)
- * @param string $httpsteps['headers']['name']
- * @param string $httpsteps['headers']['value']
- * @param string $method
- */
- private function updateHttpStepFields(array $httpsteps, $method) {
- $fields = [
- ZBX_HTTPFIELD_VARIABLE => 'variables',
- ZBX_HTTPFIELD_HEADER => 'headers',
- ZBX_HTTPFIELD_POST_FIELD => 'post_fields',
- ZBX_HTTPFIELD_QUERY_FIELD => 'query_fields'
- ];
- $httpstep_fields = [];
-
- foreach ($httpsteps as $httpstep) {
- foreach ($fields as $type => $field) {
- if (array_key_exists($field, $httpstep)) {
- $httpstep_fields[$httpstep['httpstepid']][$type] = $httpstep[$field];
- }
- }
- }
-
- if (!$httpstep_fields) {
- return;
- }
-
- $db_httpstep_fields = ($method === 'update')
- ? DB::select('httpstep_field', [
- 'output' => ['httpstep_fieldid', 'httpstepid', 'type', 'name', 'value'],
- 'filter' => ['httpstepid' => array_keys($httpstep_fields)],
- 'sortfield' => ['httpstep_fieldid']
- ])
- : [];
-
- $ins_httpstep_fields = [];
- $upd_httpstep_fields = [];
- $del_httpstep_fieldids = [];
-
- foreach ($db_httpstep_fields as $index => $db_httpstep_field) {
- if (array_key_exists($db_httpstep_field['type'], $httpstep_fields[$db_httpstep_field['httpstepid']])) {
- $httpstep_field =
- array_shift($httpstep_fields[$db_httpstep_field['httpstepid']][$db_httpstep_field['type']]);
-
- if ($httpstep_field !== null) {
- $upd_httpstep_field = [];
-
- foreach (['name', 'value'] as $field_name) {
- if ($httpstep_field[$field_name] !== $db_httpstep_field[$field_name]) {
- $upd_httpstep_field[$field_name] = $httpstep_field[$field_name];
- }
- }
-
- if ($upd_httpstep_field) {
- $upd_httpstep_fields[] = [
- 'values' => $upd_httpstep_field,
- 'where' => ['httpstep_fieldid' => $db_httpstep_field['httpstep_fieldid']]
- ];
- }
- }
- else {
- $del_httpstep_fieldids[] = $db_httpstep_field['httpstep_fieldid'];
- }
- }
- }
-
- foreach ($httpstep_fields as $httpstepid => $httpstep_fields_by_httpstep) {
- foreach ($httpstep_fields_by_httpstep as $type => $httpstep_fields_by_type) {
- foreach ($httpstep_fields_by_type as $httpstep_field) {
- $ins_httpstep_fields[] = [
- 'httpstepid' => $httpstepid,
- 'type' => $type
- ] + $httpstep_field;
- }
- }
- }
-
- if ($ins_httpstep_fields) {
- DB::insertBatch('httpstep_field', $ins_httpstep_fields);
- }
-
- if ($upd_httpstep_fields) {
- DB::update('httpstep_field', $upd_httpstep_fields);
- }
-
- if ($del_httpstep_fieldids) {
- DB::delete('httpstep_field', ['httpstep_fieldid' => $del_httpstep_fieldids]);
- }
- }
-
- /**
- * Create web scenario steps with items.
- *
- * @param array $http_test
- * @param array $websteps
- * @param array $_itemids
- *
- * @throws Exception
- */
- protected function createStepsReal(array $http_test, array $websteps, array &$_itemids): void {
- foreach ($websteps as &$webstep) {
- $webstep['httptestid'] = $http_test['httptestid'];
-
- if (array_key_exists('posts', $webstep)) {
- if (is_array($webstep['posts'])) {
- $webstep['post_fields'] = $webstep['posts'];
- $webstep['posts'] = '';
- $webstep['post_type'] = ZBX_POSTTYPE_FORM;
- }
- else {
- $webstep['post_fields'] = [];
- $webstep['post_type'] = ZBX_POSTTYPE_RAW;
- }
- }
- }
- unset($webstep);
-
- $webstepids = DB::insert('httpstep', $websteps);
-
- // if this is a template scenario, fetch the parent http items to link inherited items to them
- $parent_step_items = [];
- if (isset($http_test['templateid']) && $http_test['templateid']) {
- $parent_step_items = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid,i.key_,hsi.httpstepid'.
- ' FROM items i,httpstepitem hsi,httpstep hs'.
- ' WHERE i.itemid=hsi.itemid'.
- ' AND hsi.httpstepid=hs.httpstepid'.
- ' AND hs.httptestid='.zbx_dbstr($http_test['templateid'])
- ), 'key_');
- }
-
- $ins_httpstepitem = [];
- $ins_item_rtdata = [];
-
- foreach ($websteps as $snum => &$webstep) {
- $webstep['httpstepid'] = $webstepids[$snum];
-
- $stepitems = [
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_IN, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_IN, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 'Bps',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_IN
- ],
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_TIME, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_TIME, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 's',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_TIME
- ],
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_RSPCODE, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_RSPCODE, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'units' => '',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_RSPCODE
- ]
- ];
-
- if (!isset($http_test['delay']) || !isset($http_test['status'])) {
- $db_httptest = DBfetch(DBselect(
- 'SELECT ht.delay,ht.status'.
- ' FROM httptest ht'.
- ' WHERE ht.httptestid='.zbx_dbstr($http_test['httptestid'])
- ));
- $delay = $db_httptest['delay'];
- $status = $db_httptest['status'];
}
else {
- $delay = $http_test['delay'];
- $status = $http_test['status'];
- }
-
- $ins_items = [];
- foreach ($stepitems as $item) {
- $item['hostid'] = $http_test['hostid'];
- $item['delay'] = $delay;
- $item['type'] = ITEM_TYPE_HTTPTEST;
- $item['history'] = self::ITEM_HISTORY;
- $item['trends'] = self::ITEM_TRENDS;
- $item['status'] = (HTTPTEST_STATUS_ACTIVE == $status) ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
-
- if (isset($parent_step_items[$item['key_']])) {
- $item['templateid'] = $parent_step_items[$item['key_']]['itemid'];
- }
-
- $ins_items[] = $item;
- }
- $itemids = DB::insert('items', $ins_items);
-
- foreach ($stepitems as $inum => $item) {
- $_itemids[] = $itemids[$inum];
- $ins_httpstepitem[] = [
- 'httpstepid' => $webstep['httpstepid'],
- 'itemid' => $itemids[$inum],
- 'type' => $item['httpstepitemtype']
- ];
- }
-
- foreach ($itemids as $itemid) {
- $ins_item_rtdata[] = ['itemid' => $itemid];
- }
- }
- unset($webstep);
-
- DB::insertBatch('httpstepitem', $ins_httpstepitem);
- DB::insertBatch('item_rtdata', $ins_item_rtdata, false);
-
- $this->updateHttpStepFields($websteps, 'create');
- }
-
- /**
- * Update web scenario steps.
- *
- * @param array $httpTest
- * @param array $websteps
- * @param array $itemids_per_http_tests
- *
- * @throws Exception
- */
- protected function updateStepsReal(array $httpTest, array $websteps, array &$_itemids): void {
- $item_key_parser = new CItemKey();
-
- foreach ($websteps as &$webstep) {
- if (array_key_exists('posts', $webstep)) {
- if (is_array($webstep['posts'])) {
- $webstep['post_fields'] = $webstep['posts'];
- $webstep['posts'] = '';
- $webstep['post_type'] = ZBX_POSTTYPE_FORM;
- }
- else {
- $webstep['post_fields'] = [];
- $webstep['post_type'] = ZBX_POSTTYPE_RAW;
- }
- }
- }
- unset($webstep);
-
- foreach ($websteps as $webstep) {
- DB::update('httpstep', [
- 'values' => $webstep,
- 'where' => ['httpstepid' => $webstep['httpstepid']]
- ]);
-
- // update item keys
- $stepitems_update = [];
- $dbStepItems = DBselect(
- 'SELECT i.itemid,i.name,i.key_,hi.type'.
- ' FROM items i,httpstepitem hi'.
- ' WHERE hi.httpstepid='.zbx_dbstr($webstep['httpstepid']).
- ' AND hi.itemid=i.itemid'
- );
- while ($stepitem = DBfetch($dbStepItems)) {
- $_itemids[] = $stepitem['itemid'];
- $update_fields = [];
-
- if (isset($httpTest['name']) || isset($webstep['name'])) {
- if (!isset($httpTest['name']) || !isset($webstep['name'])) {
- $item_key_parser->parse($stepitem['key_']);
- if (!isset($httpTest['name'])) {
- $httpTest['name'] = $item_key_parser->getParam(0);
- }
- if (!isset($webstep['name'])) {
- $webstep['name'] = $item_key_parser->getParam(1);
- }
- }
-
- $update_fields['name'] = $this->getStepName($stepitem['type'], $httpTest['name'], $webstep['name']);
- if ($update_fields['name'] === $stepitem['name']) {
- unset($update_fields['name']);
- }
-
- $update_fields['key_'] = $this->getStepKey($stepitem['type'], $httpTest['name'], $webstep['name']);
- if ($update_fields['key_'] === $stepitem['key_']) {
- unset($update_fields['key_']);
- }
- }
- if (isset($httpTest['status'])) {
- $update_fields['status'] = (HTTPTEST_STATUS_ACTIVE == $httpTest['status'])
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
- }
- if (isset($httpTest['delay'])) {
- $update_fields['delay'] = $httpTest['delay'];
- }
- if (!empty($update_fields)) {
- $stepitems_update[] = [
- 'values' => $update_fields,
- 'where' => ['itemid' => $stepitem['itemid']]
- ];
- }
- }
-
- if ($stepitems_update) {
- DB::update('items', $stepitems_update);
- }
- }
-
- $this->updateHttpStepFields($websteps, 'update');
- }
-
- /**
- * Update web items after changes in web scenario.
- * This should be used, when individual steps are not being updated.
- *
- * @param array $httptest
- * @param array $db_httptest
- */
- protected function updateStepItems(array $httptest, array $db_httptest): void {
- $has_status = array_key_exists('status', $httptest);
- $has_name_changed = ($httptest['name'] !== $db_httptest['name']);
-
- $stepids = array_column($db_httptest['steps'], 'httpstepid');
-
- $stepitems = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid, hsi.httpstepid, hsi.type'.
- ' FROM items i,httpstepitem hsi'.
- ' WHERE i.itemid=hsi.itemid'.
- ' AND '.dbConditionInt('hsi.httpstepid', $stepids)
- ), 'itemid');
-
- $stepitemids = array_keys($stepitems);
-
- if ($has_status) {
- $status = ($httptest['status'] == HTTPTEST_STATUS_ACTIVE)
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
-
- DB::update('items', [
- 'values' => ['status' => $status],
- 'where' => ['itemid' => $stepitemids]
- ]);
- }
-
- if ($has_name_changed) {
- $db_websteps = zbx_toHash($db_httptest['steps'], 'httpstepid');
- $stepitems_update = [];
-
- foreach ($stepitems as $stepitem) {
- $db_webstep = $db_websteps[$stepitem['httpstepid']];
-
- $stepitems_update[] = [
- 'values' => [
- 'name' => $this->getStepName($stepitem['type'], $httptest['name'], $db_webstep['name']),
- 'key_' => $this->getStepKey($stepitem['type'], $httptest['name'], $db_webstep['name'])
- ],
- 'where' => ['itemid' => $stepitem['itemid']]
- ];
- }
-
- if ($stepitems_update) {
- DB::update('items', $stepitems_update);
- }
- }
- }
-
- /**
- * Create tags for http test and http test step items.
- * All items should belong to the same http test and should have same set of tags.
- *
- * @static
- *
- * @param array $tags New tags to save.
- * @param array $itemids List of itemids.
- */
- protected static function createItemsTags(array $tags, array $itemids): void {
- $new_tags = [];
- foreach ($tags as $tag) {
- foreach ($itemids as $itemid) {
- $new_tags[] = $tag + ['itemid' => $itemid];
+ unset($step['httpstepid']);
}
- }
-
- DB::insert('item_tag', $new_tags);
- }
- /**
- * Update step item tags.
- * Function assumes that all step items has same set of tags. Not suitable for steps from different web scenarios.
- *
- * @static
- *
- * @param array $tags New tags to save.
- * @param array $stepitemids List of step itemids to update.
- */
- protected static function updateItemsTags(array $tags, array $stepitemids): void {
- // Select tags from database.
- $db_tags_raw = DB::select('item_tag', [
- 'output' => ['itemtagid', 'tag', 'value', 'itemid'],
- 'filter' => ['itemid' => $stepitemids]
- ]);
- $db_tags = [];
- foreach ($db_tags_raw as $tag) {
- $db_tags[$tag['itemid']][$tag['tag']][$tag['value']] = $tag['itemtagid'];
- }
-
- // Make array with new tags.
- $item_tags_add = array_fill_keys($stepitemids, $tags);
-
- // Unset tags which don't need to add/delete.
- foreach ($db_tags as $stepitemid => $item_tags_del) {
- foreach ($item_tags_add[$stepitemid] as $new_tag_key => $tag_add) {
- if (array_key_exists($tag_add['tag'], $item_tags_del)
- && array_key_exists($tag_add['value'], $item_tags_del[$tag_add['tag']])) {
- unset($item_tags_add[$stepitemid][$new_tag_key],
- $db_tags[$stepitemid][$tag_add['tag']][$tag_add['value']]
- );
+ if (array_key_exists('variables', $step)) {
+ foreach ($step['variables'] as &$variable) {
+ unset($variable['httpstep_fieldid']);
}
+ unset($variable);
}
- }
- // Delete tags.
- $del_tagids = [];
- foreach ($db_tags as $db_tag) {
- foreach ($db_tag as $db_tagids) {
- if ($db_tagids) {
- $del_tagids = array_merge($del_tagids, array_values($db_tagids));
- }
- }
- }
- if ($del_tagids) {
- DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ $result[] = $step;
}
- // Add new tags.
- $new_tags = [];
- foreach ($item_tags_add as $stepitemid => $tags) {
- foreach ($tags as $tag) {
- $tag['itemid'] = $stepitemid;
- $new_tags[] = $tag;
- }
- }
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
- }
+ return $result;
}
/**
@@ -1274,7 +1713,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getTestKey(int $type, string $test_name): string {
+ protected static function getTestKey(int $type, string $test_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'web.test.in['.quoteItemKeyParam($test_name).',,bps]';
@@ -1295,7 +1734,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getTestName(int $type, string $test_name): string {
+ private static function getTestName(int $type, string $test_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'Download speed for scenario "'.$test_name.'".';
@@ -1317,7 +1756,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getStepKey(int $type, string $test_name, string $step_name): string {
+ private static function getStepKey(int $type, string $test_name, string $step_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'web.test.in['.quoteItemKeyParam($test_name).','.quoteItemKeyParam($step_name).',bps]';
@@ -1339,7 +1778,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getStepName(int $type, string $test_name, string $step_name): string {
+ private static function getStepName(int $type, string $test_name, string $step_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'Download speed for step "'.$step_name.'" of scenario "'.$test_name.'".';
@@ -1405,98 +1844,4 @@ class CHttpTestManager {
return $data;
}
-
- /**
- * Record web scenario tags into database.
- *
- * @static
- *
- * @param array $http_tests
- * @param array $http_tests[]['tags']
- * @param string $http_tests[]['tags'][]['tag']
- * @param string $http_tests[]['tags'][]['value']
- * @param string $http_tests[]['httptestid']
- */
- protected static function createHttpTestTags(array $http_tests): void {
- $new_tags = [];
- foreach ($http_tests as $http_test) {
- if (!array_key_exists('tags', $http_test)) {
- continue;
- }
-
- foreach ($http_test['tags'] as $tag) {
- $new_tags[] = $tag + ['httptestid' => $http_test['httptestid']];
- }
- }
-
- if ($new_tags) {
- DB::insert('httptest_tag', $new_tags);
- }
- }
-
- /**
- * Update web scenario tags.
- *
- * @static
- *
- * @param array $http_tests
- * @param string $http_tests[]['httptestid']
- * @param array $http_tests[]['tags']
- * @param string $http_tests[]['tags'][]['tag']
- * @param string $http_tests[]['tags'][]['value']
- */
- protected static function updateHttpTestTags(array $http_tests): void {
- $http_tests = zbx_toHash($http_tests, 'httptestid');
-
- // Select tags from database.
- $db_httptest_tags_raw = DB::select('httptest_tag', [
- 'output' => ['httptesttagid', 'httptestid', 'tag', 'value'],
- 'filter' => ['httptestid' => array_column($http_tests, 'httptestid')]
- ]);
-
- $db_httptest_tags = [];
- foreach ($db_httptest_tags_raw as $tag) {
- $db_httptest_tags[$tag['httptestid']][] = $tag;
- }
-
- // Find which tags must be added/deleted.
- $del_tagids = [];
- foreach ($db_httptest_tags as $httptestid => $db_tags) {
- if (!array_key_exists('tags', $http_tests[$httptestid])) {
- continue;
- }
-
- foreach ($db_tags as $del_tag_key => $tag_delete) {
- foreach ($http_tests[$httptestid]['tags'] as $new_tag_key => $tag_add) {
- if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
- unset($db_tags[$del_tag_key], $http_tests[$httptestid]['tags'][$new_tag_key]);
- continue 2;
- }
- }
- }
-
- if ($db_tags) {
- $del_tagids = array_merge($del_tagids, array_column($db_tags, 'httptesttagid'));
- }
- }
-
- $new_tags = [];
- foreach ($http_tests as $httptestid => $http_test) {
- if (!array_key_exists('tags', $http_test)) {
- continue;
- }
-
- foreach ($http_test['tags'] as $tag) {
- $tag['httptestid'] = $httptestid;
- $new_tags[] = $tag;
- }
- }
-
- if ($del_tagids) {
- DB::delete('httptest_tag', ['httptesttagid' => $del_tagids]);
- }
- if ($new_tags) {
- DB::insert('httptest_tag', $new_tags);
- }
- }
}
diff --git a/ui/include/classes/api/managers/CItemManager.php b/ui/include/classes/api/managers/CItemManager.php
deleted file mode 100644
index 959b60d8bde..00000000000
--- a/ui/include/classes/api/managers/CItemManager.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 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 to perform low level item related actions.
- */
-class CItemManager {
-
- /**
- * Deletes items and related entities without permission check.
- *
- * @param array $itemids
- */
- public static function delete(array $itemids) {
- global $DB;
-
- $del_itemids = [];
- $del_ruleids = [];
- $del_item_prototypeids = [];
-
- // Selecting all inherited items.
- $parent_itemids = array_flip($itemids);
- do {
- $db_items = DBselect(
- 'SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', array_keys($parent_itemids))
- );
-
- $del_itemids += $parent_itemids;
- $parent_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $parent_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($parent_itemids);
-
- // Selecting all dependent items.
- // Note: We are not separating normal from discovered items at this point.
- $dep_itemids = [
- ZBX_FLAG_DISCOVERY_NORMAL => $del_itemids,
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- do {
- $db_items = DBselect(
- 'SELECT i.itemid,i.flags'.
- ' FROM items i'.
- ' WHERE i.type='.ITEM_TYPE_DEPENDENT.
- ' AND '.dbConditionInt('i.master_itemid',
- array_keys($dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL]
- + $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED]
- + $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE]
- )
- )
- );
-
- $dep_itemids = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- while ($db_item = DBfetch($db_items)) {
- switch ($db_item['flags']) {
- case ZBX_FLAG_DISCOVERY_NORMAL:
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL][$db_item['itemid']] = true;
- }
- break;
-
- case ZBX_FLAG_DISCOVERY_RULE:
- $dep_itemids[ZBX_FLAG_DISCOVERY_RULE][$db_item['itemid']] = true;
- break;
-
- case ZBX_FLAG_DISCOVERY_CREATED:
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED][$db_item['itemid']] = true;
- }
- break;
-
- case ZBX_FLAG_DISCOVERY_PROTOTYPE:
- $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE][$db_item['itemid']] = true;
- break;
- }
- }
-
- $del_itemids += $dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL];
- $del_itemids += $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED];
- $del_ruleids += $dep_itemids[ZBX_FLAG_DISCOVERY_RULE];
- $del_item_prototypeids += $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE];
-
- } while ($dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL]
- || $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED]
- || $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE]
- );
-
- $del_itemids = array_keys($del_itemids);
-
- if ($del_ruleids) {
- CDiscoveryRuleManager::delete(array_keys($del_ruleids));
- }
-
- if ($del_item_prototypeids) {
- CItemPrototypeManager::delete(array_keys($del_item_prototypeids));
- }
-
- // Deleting graphs and graph prototypes, which will remain without items.
- $db_graphs = DBselect(
- 'SELECT DISTINCT gi.graphid'.
- ' FROM graphs_items gi'.
- ' WHERE '.dbConditionInt('gi.itemid', $del_itemids).
- ' AND NOT EXISTS ('.
- 'SELECT NULL'.
- ' FROM graphs_items gii'.
- ' WHERE gii.graphid=gi.graphid'.
- ' AND '.dbConditionInt('gii.itemid', $del_itemids, true).
- ')'
- );
-
- $del_graphids = [];
-
- while ($db_graph = DBfetch($db_graphs)) {
- $del_graphids[] = $db_graph['graphid'];
- }
-
- if ($del_graphids) {
- CGraphManager::delete($del_graphids);
- }
-
- // Cleanup ymin_itemid and ymax_itemid fields for graphs and graph prototypes.
- DB::update('graphs', [
- 'values' => [
- 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymin_itemid' => null
- ],
- 'where' => ['ymin_itemid' => $del_itemids]
- ]);
-
- DB::update('graphs', [
- 'values' => [
- 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymax_itemid' => null
- ],
- 'where' => ['ymax_itemid' => $del_itemids]
- ]);
-
- // Deleting triggers and trigger prototypes.
- $db_triggers = DBselect(
- 'SELECT DISTINCT t.triggerid,t.flags'.
- ' FROM triggers t,functions f'.
- ' WHERE t.triggerid=f.triggerid'.
- ' AND '.dbConditionInt('f.itemid', $del_itemids)
- );
-
- $del_triggerids = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- while ($db_trigger = DBfetch($db_triggers)) {
- $del_triggerids[$db_trigger['flags']][] = $db_trigger['triggerid'];
- }
-
- if ($del_triggerids[ZBX_FLAG_DISCOVERY_NORMAL] || $del_triggerids[ZBX_FLAG_DISCOVERY_CREATED]) {
- CTriggerManager::delete(array_merge(
- $del_triggerids[ZBX_FLAG_DISCOVERY_NORMAL],
- $del_triggerids[ZBX_FLAG_DISCOVERY_CREATED]
- ));
- }
-
- if ($del_triggerids[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- CTriggerPrototypeManager::delete($del_triggerids[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
- }
-
- DB::delete('profiles', [
- 'idx' => 'web.favorite.graphids',
- 'source' => 'itemid',
- 'value_id' => $del_itemids
- ]);
-
- $table_names = ['events'];
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) != 0) {
- array_push($table_names, 'history', 'history_str', 'history_uint', 'history_log', 'history_text');
- }
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) != 0) {
- array_push($table_names,'trends', 'trends_uint');
- }
-
- if ($DB['TYPE'] === ZBX_DB_POSTGRESQL) {
- if (CHousekeepingHelper::get(CHousekeepingHelper::DB_EXTENSION) === ZBX_DB_EXTENSION_TIMESCALEDB) {
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) != 0
- && CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL) == 1) {
- $table_names = array_diff($table_names,
- ['history', 'history_str', 'history_uint', 'history_log', 'history_text']
- );
- }
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) != 0
- && CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL) == 1) {
- $table_names = array_diff($table_names, ['trends', 'trends_uint']);
- }
- }
- }
-
- $ins_housekeeper = [];
-
- foreach ($del_itemids as $del_itemid) {
- foreach ($table_names as $table_name) {
- $ins_housekeeper[] = [
- 'tablename' => $table_name,
- 'field' => 'itemid',
- 'value' => $del_itemid
- ];
-
- if (count($ins_housekeeper) == ZBX_DB_MAX_INSERTS) {
- DB::insertBatch('housekeeper', $ins_housekeeper);
- $ins_housekeeper = [];
- }
- }
- }
-
- if ($ins_housekeeper) {
- DB::insertBatch('housekeeper', $ins_housekeeper);
- }
-
- DB::delete('httpstepitem', ['itemid' => $del_itemids]);
- DB::delete('httptestitem', ['itemid' => $del_itemids]);
-
- DB::delete('item_tag', ['itemid' => $del_itemids]);
- DB::delete('item_preproc', ['itemid' => $del_itemids]);
- DB::update('items', [
- 'values' => ['templateid' => 0, 'master_itemid' => 0],
- 'where' => ['itemid' => $del_itemids]
- ]);
- DB::delete('items', ['itemid' => $del_itemids]);
- }
-}
diff --git a/ui/include/classes/api/managers/CItemPrototypeManager.php b/ui/include/classes/api/managers/CItemPrototypeManager.php
deleted file mode 100644
index 8741d30fbf5..00000000000
--- a/ui/include/classes/api/managers/CItemPrototypeManager.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 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 to perform low level item related actions.
- */
-class CItemPrototypeManager {
-
- /**
- * Deletes item prototypes and related entities without permission check.
- *
- * @param array $itemids
- */
- public static function delete(array $itemids) {
- $del_itemids = [];
-
- // Selecting all inherited items.
- $parent_itemids = array_flip($itemids);
- do {
- $db_items = DBselect(
- 'SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', array_keys($parent_itemids))
- );
-
- $del_itemids += $parent_itemids;
- $parent_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $parent_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($parent_itemids);
-
- // Selecting all dependent items.
- $dep_itemids = $del_itemids;
- $del_itemids = [];
-
- do {
- $db_items = DBselect(
- 'SELECT i.itemid'.
- ' FROM items i'.
- ' WHERE i.type='.ITEM_TYPE_DEPENDENT.
- ' AND '.dbConditionInt('i.master_itemid', array_keys($dep_itemids))
- );
-
- $del_itemids += $dep_itemids;
- $dep_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($dep_itemids);
-
- $del_itemids = array_keys($del_itemids);
-
- // Lock item prototypes before delete to prevent server from adding new LLD elements.
- DBselect(
- 'SELECT NULL'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.itemid', $del_itemids).
- ' FOR UPDATE'
- );
-
- // Deleting graph prototypes, which will remain without item prototypes.
- $db_graphs = DBselect(
- 'SELECT DISTINCT gi.graphid'.
- ' FROM graphs_items gi'.
- ' WHERE '.dbConditionInt('gi.itemid', $del_itemids).
- ' AND NOT EXISTS ('.
- 'SELECT NULL'.
- ' FROM graphs_items gii,items i'.
- ' WHERE gi.graphid=gii.graphid'.
- ' AND gii.itemid=i.itemid'.
- ' AND '.dbConditionInt('gii.itemid', $del_itemids, true).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_PROTOTYPE]).
- ')'
- );
-
- $del_graphids = [];
-
- while ($db_graph = DBfetch($db_graphs)) {
- $del_graphids[] = $db_graph['graphid'];
- }
-
- if ($del_graphids) {
- CGraphPrototypeManager::delete($del_graphids);
- }
-
- // Cleanup ymin_itemid and ymax_itemid fields for graphs and graph prototypes.
- DB::update('graphs', [
- 'values' => [
- 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymin_itemid' => null
- ],
- 'where' => ['ymin_itemid' => $del_itemids]
- ]);
-
- DB::update('graphs', [
- 'values' => [
- 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymax_itemid' => null
- ],
- 'where' => ['ymax_itemid' => $del_itemids]
- ]);
-
- // Deleting discovered items.
- $del_discovered_itemids = DBfetchColumn(DBselect(
- 'SELECT id.itemid FROM item_discovery id WHERE '.dbConditionInt('id.parent_itemid', $del_itemids)
- ), 'itemid');
-
- if ($del_discovered_itemids) {
- CItemManager::delete($del_discovered_itemids);
- }
-
- // Deleting trigger prototypes.
- $del_triggerids = DBfetchColumn(DBselect(
- 'SELECT DISTINCT f.triggerid'.
- ' FROM functions f'.
- ' WHERE '.dbConditionInt('f.itemid', $del_itemids)
- ), 'triggerid');
-
- if ($del_triggerids) {
- CTriggerPrototypeManager::delete($del_triggerids);
- }
-
- DB::delete('item_tag', ['itemid' => $del_itemids]);
- DB::delete('item_preproc', ['itemid' => $del_itemids]);
- DB::update('items', [
- 'values' => ['templateid' => 0, 'master_itemid' => 0],
- 'where' => ['itemid' => $del_itemids]
- ]);
- DB::delete('items', ['itemid' => $del_itemids]);
- }
-}
diff --git a/ui/include/classes/api/services/CConfiguration.php b/ui/include/classes/api/services/CConfiguration.php
index 7e6953e79cb..fe93d89239d 100644
--- a/ui/include/classes/api/services/CConfiguration.php
+++ b/ui/include/classes/api/services/CConfiguration.php
@@ -209,7 +209,9 @@ class CConfiguration extends CApiService {
->setStrict(true)
->validate($data, '/');
- foreach (['1.0', '2.0', '3.0', '3.2', '3.4', '4.0', '4.2', '4.4', '5.0', '5.2', '5.4', '6.0'] as $version) {
+ $all_versions = ['1.0', '2.0', '3.0', '3.2', '3.4', '4.0', '4.2', '4.4', '5.0', '5.2', '5.4', '6.0', '6.2'];
+
+ foreach ($all_versions as $version) {
if ($data['zabbix_export']['version'] !== $version) {
continue;
}
diff --git a/ui/include/classes/api/services/CDiscoveryRule.php b/ui/include/classes/api/services/CDiscoveryRule.php
index 4a2b123e38a..55bd23e9044 100644
--- a/ui/include/classes/api/services/CDiscoveryRule.php
+++ b/ui/include/classes/api/services/CDiscoveryRule.php
@@ -22,7 +22,7 @@
/**
* Class containing methods for operations with discovery rules.
*/
-class CDiscoveryRule extends CItemGeneral {
+class CDiscoveryRule extends CItemGeneralOld {
public const ACCESS_RULES = parent::ACCESS_RULES + [
'copy' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
@@ -712,6 +712,77 @@ class CDiscoveryRule extends CItemGeneral {
}
/**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ii.itemid,h.status AS host_status'.
+ ' FROM items i,items ii,hosts h'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND ii.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]).
+ $hostids_condition
+ );
+
+ $upd_items = [];
+ $ruleids = [];
+
+ while ($row = DBfetch($result)) {
+ $upd_item = [
+ 'templateid' => 0,
+ 'valuemapid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $upd_item += ['uuid' => generateUuidV4()];
+ }
+
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $row['itemid']]
+ ];
+
+ $ruleids[] = $row['itemid'];
+ }
+
+ if ($upd_items) {
+ DB::update('items', $upd_items);
+
+ /*
+ * TODO: The trigger prototypes and graphs also should be updated here when new audit log will be added for
+ * them.
+ */
+ CItemPrototype::unlinkTemplateObjects($ruleids);
+ CHostPrototype::unlinkTemplateObjects($ruleids);
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $ruleids = DBfetchColumn(DBselect(
+ 'SELECT ii.itemid'.
+ ' FROM items i,items ii'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]).
+ $hostids_condition
+ ), 'itemid');
+
+ if ($ruleids) {
+ CDiscoveryRuleManager::delete($ruleids);
+ }
+ }
+
+ /**
* Copies all of the triggers from the source discovery to the target discovery rule.
*
* @param array $src_discovery The source discovery rule to copy from.
@@ -2124,14 +2195,14 @@ class CDiscoveryRule extends CItemGeneral {
// fetch source and destination hosts
$hosts = API::Host()->get([
- 'hostids' => [$srcDiscovery['hostid'], $hostid],
'output' => ['hostid', 'host', 'name', 'status'],
- 'selectInterfaces' => API_OUTPUT_EXTEND,
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ 'hostids' => [$srcDiscovery['hostid'], $hostid],
'templated_hosts' => true,
'preservekeys' => true
]);
- $srcHost = $hosts[$srcDiscovery['hostid']];
- $dstHost = $hosts[$hostid];
+ $src_host = $hosts[$srcDiscovery['hostid']];
+ $dst_host = $hosts[$hostid];
$dstDiscovery = $srcDiscovery;
$dstDiscovery['hostid'] = $hostid;
@@ -2163,9 +2234,9 @@ class CDiscoveryRule extends CItemGeneral {
}
// if this is a plain host, map discovery interfaces
- if ($srcHost['status'] != HOST_STATUS_TEMPLATE) {
+ if ($src_host['status'] != HOST_STATUS_TEMPLATE) {
// find a matching interface
- $interface = self::findInterfaceForItem($dstDiscovery['type'], $dstHost['interfaces']);
+ $interface = self::findInterfaceForItem($dstDiscovery['type'], $dst_host['interfaces']);
if ($interface) {
$dstDiscovery['interfaceid'] = $interface['interfaceid'];
}
@@ -2173,7 +2244,7 @@ class CDiscoveryRule extends CItemGeneral {
elseif ($interface !== false) {
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
'Cannot find host interface on "%1$s" for item key "%2$s".',
- $dstHost['name'],
+ $dst_host['name'],
$dstDiscovery['key_']
));
}
@@ -2203,22 +2274,21 @@ class CDiscoveryRule extends CItemGeneral {
$dstDiscovery['itemid'] = $newDiscovery['itemids'][0];
// copy prototypes
- $new_prototypeids = $this->copyItemPrototypes($srcDiscovery, $dstDiscovery, $dstHost);
+ $this->copyItemPrototypes($srcDiscovery['itemid'], $src_host, $dstDiscovery['itemid'], $dst_host);
- // if there were prototypes defined, clone everything else
- if ($new_prototypeids) {
- // fetch new prototypes
- $dstDiscovery['items'] = API::ItemPrototype()->get([
- 'output' => ['itemid', 'key_'],
- 'itemids' => $new_prototypeids,
- 'preservekeys' => true
- ]);
+ // fetch new prototypes
+ $dstDiscovery['items'] = API::ItemPrototype()->get([
+ 'output' => ['itemid', 'key_'],
+ 'discoveryids' => $dstDiscovery['itemid'],
+ 'preservekeys' => true
+ ]);
+ if ($dstDiscovery['items']) {
// copy graphs
$this->copyGraphPrototypes($srcDiscovery, $dstDiscovery);
// copy triggers
- $this->copyTriggerPrototypes($srcDiscovery, $srcHost, $dstHost);
+ $this->copyTriggerPrototypes($srcDiscovery, $src_host, $dst_host);
}
// copy host prototypes
@@ -2228,203 +2298,256 @@ class CDiscoveryRule extends CItemGeneral {
}
/**
- * Copies all of the item prototypes from the source discovery to the target
- * discovery rule. Return array of created item prototype ids.
+ * Create copies of items prototypes from the given source LLD rule to the given destination host or template.
*
- * @throws APIException if prototype saving fails
- *
- * @param array $srcDiscovery The source discovery rule to copy from
- * @param array $dstDiscovery The target discovery rule to copy to
- * @param array $dstHost The target host to copy the discovery rule to
+ * @param string $src_ruleid
+ * @param array $src_host
+ * @param array $src_host['interfaces']
+ * @param string $dst_ruleid
+ * @param array $dst_host
+ * @param string $dst_host['hostid']
+ * @param string $dst_host['host']
+ * @param array $dst_host['interfaces']
*
- * @return array
+ * @throws APIException
*/
- protected function copyItemPrototypes(array $srcDiscovery, array $dstDiscovery, array $dstHost) {
- $item_prototypes = API::ItemPrototype()->get([
- 'output' => ['itemid', 'type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status',
- 'value_type', 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype',
- 'username', 'password', 'publickey', 'privatekey', 'interfaceid', 'port', 'description', 'jmx_endpoint',
- 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes',
- 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method',
- 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host',
- 'allow_traps', 'discover', 'parameters'
+ private static function copyItemPrototypes(string $src_ruleid, array $src_host, string $dst_ruleid,
+ array $dst_host): void {
+ $src_items = API::ItemPrototype()->get([
+ 'output' => ['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover',
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
],
'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
'selectTags' => ['tag', 'value'],
- 'selectValueMap' => ['name'],
- 'discoveryids' => $srcDiscovery['itemid'],
+ 'discoveryids' => $src_ruleid,
'preservekeys' => true
]);
- $new_itemids = [];
- $itemkey_to_id = [];
- $create_items = [];
- $src_valuemap_names = [];
- $valuemap_map = [];
- foreach ($item_prototypes as $item_prototype) {
- if ($item_prototype['valuemap']) {
- $src_valuemap_names[] = $item_prototype['valuemap']['name'];
- }
+ if (!$src_items) {
+ return;
}
- if ($src_valuemap_names) {
- $valuemap_map = array_column(API::ValueMap()->get([
- 'output' => ['valuemapid', 'name'],
- 'hostids' => $dstHost['hostid'],
- 'filter' => ['name' => $src_valuemap_names]
- ]), 'valuemapid', 'name');
- }
+ $src_itemids = array_fill_keys(array_keys($src_items), true);
+ $src_valuemapids = [];
+ $src_interfaceids = [];
+ $src_dep_items = [];
+ $dep_itemids = [];
+
+ foreach ($src_items as $itemid => $item) {
+ if ($item['valuemapid'] != 0) {
+ $src_valuemapids[$item['valuemapid']] = true;
+ }
- if ($item_prototypes) {
- $create_order = [];
- $src_itemid_to_key = [];
- $unresolved_master_itemids = [];
+ if ($item['interfaceid'] != 0) {
+ $src_interfaceids[$item['interfaceid']] = true;
+ }
+
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if (array_key_exists($item['master_itemid'], $src_itemids)) {
+ $src_dep_items[$item['master_itemid']][] = $item;
- // Gather all master item IDs and check if master item IDs already belong to item prototypes.
- foreach ($item_prototypes as $itemid => $item_prototype) {
- if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT
- && !array_key_exists($item_prototype['master_itemid'], $item_prototypes)) {
- $unresolved_master_itemids[$item_prototype['master_itemid']] = true;
+ unset($src_items[$itemid]);
+ }
+ else {
+ $dep_itemids[$item['master_itemid']][] = $item['itemid'];
}
}
+ }
- $items = [];
+ $valuemap_links = [];
- // It's possible that master items are non-prototype items.
- if ($unresolved_master_itemids) {
- $items = API::Item()->get([
- 'output' => ['itemid'],
- 'itemids' => array_keys($unresolved_master_itemids),
- 'webitems' => true,
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true
- ]);
+ if ($src_valuemapids) {
+ $src_valuemaps = API::ValueMap()->get([
+ 'output' => ['valuemapid', 'name'],
+ 'valuemapids' => array_keys($src_valuemapids)
+ ]);
- foreach ($items as $item) {
- if (array_key_exists($item['itemid'], $unresolved_master_itemids)) {
- unset($unresolved_master_itemids[$item['itemid']]);
+ $dst_valuemaps = API::ValueMap()->get([
+ 'output' => ['valuemapid', 'hostid', 'name'],
+ 'hostids' => $dst_host['hostid'],
+ 'filter' => ['name' => array_unique(array_column($src_valuemaps, 'name'))]
+ ]);
+
+ $dst_valuemapids = [];
+
+ foreach ($dst_valuemaps as $dst_valuemap) {
+ $dst_valuemapids[$dst_valuemap['name']][$dst_valuemap['hostid']] = $dst_valuemap['valuemapid'];
+ }
+
+ foreach ($src_valuemaps as $src_valuemap) {
+ if (array_key_exists($src_valuemap['name'], $dst_valuemapids)) {
+ foreach ($dst_valuemapids[$src_valuemap['name']] as $dst_hostid => $dst_valuemapid) {
+ $valuemap_links[$src_valuemap['valuemapid']][$dst_hostid] = $dst_valuemapid;
}
}
+ }
+ }
- // If still there are IDs left, there's nothing more we can do.
- if ($unresolved_master_itemids) {
- reset($unresolved_master_itemids);
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _s('Item "%1$s" does not exist or you have no access to this item',
- key($unresolved_master_itemids)
- )));
+ $interface_links = [];
+ $dst_interfaceids = [];
+
+ if ($src_interfaceids) {
+ $src_interfaces = [];
+
+ foreach ($src_host['interfaces'] as $src_interface) {
+ if (array_key_exists($src_interface['interfaceid'], $src_interfaceids)) {
+ $src_interfaces[$src_interface['interfaceid']] =
+ array_diff_key($src_interface, array_flip(['interfaceid']));
}
}
- foreach ($item_prototypes as $itemid => $item_prototype) {
- $dependency_level = 0;
- $master_item_prototype = $item_prototype;
- $src_itemid_to_key[$itemid] = $item_prototype['key_'];
+ foreach ($dst_host['interfaces'] as $dst_interface) {
+ $dst_interfaceid = $dst_interface['interfaceid'];
+ unset($dst_interface['interfaceid']);
- while ($master_item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
- if (array_key_exists($master_item_prototype['master_itemid'], $item_prototypes)) {
- $master_item_prototype = $item_prototypes[$master_item_prototype['master_itemid']];
- ++$dependency_level;
- }
- else {
- break;
+ foreach ($src_interfaces as $src_interfaceid => $src_interface) {
+ if ($src_interface == $dst_interface) {
+ $interface_links[$src_interfaceid][$dst_host['hostid']] = $dst_interfaceid;
}
}
- $create_order[$itemid] = $dependency_level;
+ if ($dst_interface['main'] == INTERFACE_PRIMARY) {
+ $dst_interfaceids[$dst_host['hostid']][$dst_interface['type']] = $dst_interfaceid;
+ }
}
- asort($create_order);
+ }
- $current_dependency = reset($create_order);
+ $master_item_links = [];
- foreach ($create_order as $key => $dependency_level) {
- if ($current_dependency != $dependency_level && $create_items) {
- $current_dependency = $dependency_level;
- $created_itemids = API::ItemPrototype()->create($create_items);
+ if ($dep_itemids) {
+ $master_items = API::Item()->get([
+ 'output' => ['itemid', 'key_'],
+ 'itemids' => array_keys($dep_itemids)
+ ]);
- if (!$created_itemids) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
- }
+ $options = $dst_host['status'] == HOST_STATUS_TEMPLATE
+ ? ['templateids' => $dst_host['hostid']]
+ : ['hostids' => $dst_host['hostid']];
- $created_itemids = $created_itemids['itemids'];
- $new_itemids = array_merge($new_itemids, $created_itemids);
+ $dst_master_items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'key_'],
+ 'filter' => ['key_' => array_unique(array_column($master_items, 'key_'))]
+ ] + $options);
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
- }
+ $dst_master_itemids = [];
- $create_items = [];
- }
+ foreach ($dst_master_items as $item) {
+ $dst_master_itemids[$item['hostid']][$item['key_']] = $item['itemid'];
+ }
- $item_prototype = $item_prototypes[$key];
- $item_prototype['ruleid'] = $dstDiscovery['itemid'];
- $item_prototype['hostid'] = $dstDiscovery['hostid'];
+ foreach ($master_items as $item) {
+ if (array_key_exists($dst_host['hostid'], $dst_master_itemids)
+ && array_key_exists($item['key_'], $dst_master_itemids[$dst_host['hostid']])) {
+ $master_item_links[$item['itemid']][$dst_host['hostid']] =
+ $dst_master_itemids[$dst_host['hostid']][$item['key_']];
+ }
+ else {
+ $src_itemid = reset($dep_itemids[$item['itemid']]);
- if ($item_prototype['valuemapid'] != 0) {
- $item_prototype['valuemapid'] = array_key_exists($item_prototype['valuemap']['name'], $valuemap_map)
- ? $valuemap_map[$item_prototype['valuemap']['name']]
- : 0;
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Cannot copy item prototype with key "%1$s" without its master item with key "%2$s".',
+ $src_items[$src_itemid]['key_'], $item['key_']
+ ));
}
+ }
+ }
- // map prototype interfaces
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- // find a matching interface
- $interface = self::findInterfaceForItem($item_prototype['type'], $dstHost['interfaces']);
- if ($interface) {
- $item_prototype['interfaceid'] = $interface['interfaceid'];
+ do {
+ $dst_items = [];
+
+ foreach ($src_items as $src_item) {
+ $dst_item = array_diff_key($src_item, array_flip(['itemid']));
+
+ if ($src_item['valuemapid'] != 0) {
+ if (array_key_exists($src_item['valuemapid'], $valuemap_links)
+ && array_key_exists($dst_host['hostid'], $valuemap_links[$src_item['valuemapid']])) {
+ $dst_item['valuemapid'] = $valuemap_links[$src_item['valuemapid']][$dst_host['hostid']];
}
- // no matching interface found, throw an error
- elseif ($interface !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s(
- 'Cannot find host interface on "%1$s" for item key "%2$s".',
- $dstHost['name'],
- $item_prototype['key_']
- ));
+ else {
+ $dst_item['valuemapid'] = 0;
}
}
- if (!$item_prototype['preprocessing']) {
- unset($item_prototype['preprocessing']);
- }
-
- if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
- $master_itemid = $item_prototype['master_itemid'];
+ $dst_item['interfaceid'] = 0;
- if (array_key_exists($master_itemid, $src_itemid_to_key)) {
- $src_item_key = $src_itemid_to_key[$master_itemid];
- $item_prototype['master_itemid'] = $itemkey_to_id[$src_item_key];
+ if ($src_item['interfaceid'] != 0) {
+ if (array_key_exists($src_item['interfaceid'], $interface_links)
+ && array_key_exists($dst_host['hostid'], $interface_links[$src_item['interfaceid']])) {
+ $dst_item['interfaceid'] = $interface_links[$src_item['interfaceid']][$dst_host['hostid']];
}
else {
- // It's a non-prototype item, so look for it on destination host.
- $dst_item = get_same_item_for_host($items[$master_itemid], $dstHost['hostid']);
-
- if (!$dst_item) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
+ $type = itemTypeInterface($src_item['type']);
+
+ if (in_array($type,
+ [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI]
+ )) {
+ if (array_key_exists($dst_host['hostid'], $dst_interfaceids)
+ && array_key_exists($type, $dst_interfaceids[$dst_host['hostid']])) {
+ $dst_item['interfaceid'] = $dst_interfaceids[$dst_host['hostid']][$type];
+ }
+ else {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Cannot find host interface on "%1$s" for item prototype with key "%2$s".',
+ $dst_host['host'], $src_item['key_']
+ ));
+ }
}
-
- $item_prototype['master_itemid'] = $dst_item['itemid'];
}
}
- else {
- unset($item_prototype['master_itemid']);
+
+ if ($src_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dst_item['master_itemid'] = $master_item_links[$src_item['master_itemid']][$dst_host['hostid']];
}
- unset($item_prototype['templateid']);
- $create_items[] = $item_prototype;
+ $dst_items[] = ['hostid' => $dst_host['hostid'], 'ruleid' => $dst_ruleid] + $dst_item;
}
- if ($create_items) {
- $created_itemids = API::ItemPrototype()->create($create_items);
+ $response = API::ItemPrototype()->create($dst_items);
- if (!$created_itemids) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
- }
+ $_src_items = [];
+
+ if ($src_dep_items) {
+ foreach ($src_items as $src_item) {
+ $dst_itemid = array_shift($response['itemids']);
+
+ if (array_key_exists($src_item['itemid'], $src_dep_items)) {
+ $master_item_links[$src_item['itemid']][$dst_host['hostid']] = $dst_itemid;
- $new_itemids = array_merge($new_itemids, $created_itemids['itemids']);
+ $_src_items = array_merge($_src_items, $src_dep_items[$src_item['itemid']]);
+ unset($src_dep_items[$src_item['itemid']]);
+ }
+ }
}
- }
- return $new_itemids;
+ $src_items = $_src_items;
+ } while ($src_items);
}
/**
diff --git a/ui/include/classes/api/services/CHost.php b/ui/include/classes/api/services/CHost.php
index a5cb153d28b..cfe7b8f5e50 100644
--- a/ui/include/classes/api/services/CHost.php
+++ b/ui/include/classes/api/services/CHost.php
@@ -1479,24 +1479,29 @@ class CHost extends CHostGeneral {
}
// delete the items
- $del_items = API::Item()->get([
- 'output' => [],
- 'templateids' => $hostids,
- 'nopermissions' => true,
+ $db_items = DB::select('items', [
+ 'output' => ['itemid', 'name'],
+ 'filter' => [
+ 'hostid' => $hostids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => CItem::SUPPORTED_ITEM_TYPES
+ ],
'preservekeys' => true
]);
- if ($del_items) {
- CItemManager::delete(array_keys($del_items));
- }
- // delete web tests
- $delHttptests = [];
- $dbHttptests = get_httptests_by_hostid($hostids);
- while ($dbHttptest = DBfetch($dbHttptests)) {
- $delHttptests[$dbHttptest['httptestid']] = $dbHttptest['httptestid'];
+ if ($db_items) {
+ CItem::deleteForce($db_items);
}
- if (!empty($delHttptests)) {
- API::HttpTest()->delete($delHttptests, true);
+
+ // delete web scenarios
+ $db_httptests = DB::select('httptest', [
+ 'output' => ['httptestid', 'name'],
+ 'filter' => ['hostid' => $hostids],
+ 'preservekeys' => true
+ ]);
+
+ if ($db_httptests) {
+ CHttpTest::deleteForce($db_httptests);
}
// delete host from maps
diff --git a/ui/include/classes/api/services/CHostGeneral.php b/ui/include/classes/api/services/CHostGeneral.php
index da3954aae21..8de2ef3c2fb 100644
--- a/ui/include/classes/api/services/CHostGeneral.php
+++ b/ui/include/classes/api/services/CHostGeneral.php
@@ -415,7 +415,8 @@ abstract class CHostGeneral extends CHostBase {
* @param array|null $hostids
* @param bool $clear
*/
- protected static function unlinkTemplatesObjects(array $templateids, array $hostids = null, bool $clear = false): void {
+ protected static function unlinkTemplatesObjects(array $templateids, array $hostids = null,
+ bool $clear = false): void {
$flags = ($clear)
? [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE]
: [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, ZBX_FLAG_DISCOVERY_PROTOTYPE];
@@ -584,220 +585,15 @@ abstract class CHostGeneral extends CHostBase {
}
}
- // items, discovery rules
- $upd_items = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
- $parent_itemids = [];
- $item_prototypeids = [];
-
- $sqlFrom = ' items i1,items i2,hosts h';
- $sqlWhere = ' i2.itemid=i1.templateid'.
- ' AND '.dbConditionInt('i2.hostid', $templateids).
- ' AND '.dbConditionInt('i1.flags', $flags).
- ' AND h.hostid=i1.hostid';
-
- if (!is_null($hostids)) {
- $sqlWhere .= ' AND '.dbConditionInt('i1.hostid', $hostids);
- }
- $sql = 'SELECT DISTINCT i1.itemid,i1.flags,h.status as host_status,i1.type,i1.valuemapid'.
- ' FROM '.$sqlFrom.
- ' WHERE '.$sqlWhere;
-
- $dbItems = DBSelect($sql);
-
- while ($item = DBfetch($dbItems)) {
- if ($clear) {
- $upd_items[$item['flags']][$item['itemid']] = true;
- }
- else {
- $upd_item = ['templateid' => 0];
- if ($item['host_status'] == HOST_STATUS_TEMPLATE && $item['type'] != ITEM_TYPE_HTTPTEST) {
- $upd_item['uuid'] = generateUuidV4();
- }
-
- if ($item['valuemapid'] !== '0') {
- $upd_item['valuemapid'] = '0';
-
- if ($item['host_status'] == HOST_STATUS_TEMPLATE) {
- $parent_itemids[] = $item['itemid'];
- }
- elseif ($item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $item['itemid'];
- }
- }
-
- $upd_items[$item['flags']][$item['itemid']] = [
- 'values' => $upd_item,
- 'where' => ['itemid' => $item['itemid']]
- ];
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- if ($clear) {
- CDiscoveryRuleManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_RULE]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]) {
- if ($clear) {
- CItemManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_NORMAL]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- if ($clear) {
- CItemPrototypeManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
- }
- }
-
- if ($parent_itemids) {
- $child_upd_items = [];
-
- while ($parent_itemids) {
- $result = DBselect(
- 'SELECT i.itemid,i.flags,h.status AS host_status'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid'.
- ' AND '.dbConditionId('i.templateid', $parent_itemids)
- );
-
- $parent_itemids = [];
-
- while ($row = DBfetch($result)) {
- $parent_itemids[] = $row['itemid'];
-
- $child_upd_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
-
- if (in_array($row['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
- && $row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $row['itemid'];
- }
- }
- }
-
- if ($child_upd_items) {
- DB::update('items', $child_upd_items);
- }
- }
-
- if ($item_prototypeids) {
- $options = [
- 'output' => ['itemid'],
- 'filter' => ['parent_itemid' => $item_prototypeids],
- 'sortfield' => ['itemid'],
- 'sortorder' => [ZBX_SORT_DOWN]
- ];
- $result = DBselect(DB::makeSql('item_discovery', $options));
-
- $upd_discovered_items = [];
-
- while ($row = DBfetch($result)) {
- $upd_discovered_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
- }
-
- if ($upd_discovered_items) {
- DB::update('items', $upd_discovered_items);
- }
- }
-
- // host prototypes
- if (!$clear && $upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- $host_prototypes = DBSelect(
- 'SELECT DISTINCT h.hostid,h3.status as host_status'.
- ' FROM hosts h'.
- ' INNER JOIN host_discovery hd ON h.hostid=hd.hostid'.
- ' INNER JOIN hosts h2 ON h.templateid=h2.hostid'.
- ' INNER JOIN host_discovery hd2 ON h.hostid=hd.hostid'.
- ' INNER JOIN items i ON hd.parent_itemid=i.itemid'.
- ' INNER JOIN hosts h3 ON i.hostid=h3.hostid'.
- ' WHERE '.dbConditionInt('hd.parent_itemid', array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]))
- );
-
- $upd_host_prototypes = [];
-
- while ($host_prototype = DBfetch($host_prototypes)) {
- $upd_host_prototype = ['templateid' => 0];
- if ($host_prototype['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_host_prototype['uuid'] = generateUuidV4();
- }
-
- $upd_host_prototypes[$host_prototype['hostid']] = [
- 'values' => $upd_host_prototype,
- 'where' => ['hostid' => $host_prototype['hostid']]
- ];
- }
-
- if ($upd_host_prototypes) {
- DB::update('hosts', $upd_host_prototypes);
- DB::update('group_prototype', [
- 'values' => ['templateid' => 0],
- 'where' => ['hostid' => array_keys($upd_host_prototypes)]
- ]);
- }
- }
-
- // http tests
- $upd_httptests = [];
-
- $sqlWhere = '';
- if ($hostids !== null) {
- $sqlWhere = ' AND '.dbConditionInt('ht1.hostid', $hostids);
- }
- $sql = 'SELECT DISTINCT ht1.httptestid,h.status as host_status'.
- ' FROM httptest ht1,httptest ht2,hosts h'.
- ' WHERE ht1.templateid=ht2.httptestid'.
- ' AND ht1.hostid=h.hostid'.
- ' AND '.dbConditionInt('ht2.hostid', $templateids).
- $sqlWhere;
-
- $httptests = DBSelect($sql);
-
- while ($httptest = DBfetch($httptests)) {
- if ($clear) {
- $upd_httptests[$httptest['httptestid']] = true;
- }
- else {
- $upd_httptest = ['templateid' => 0];
- if ($httptest['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_httptest['uuid'] = generateUuidV4();
- }
-
- $upd_httptests[$httptest['httptestid']] = [
- 'values' => $upd_httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ];
- }
+ if ($clear) {
+ CDiscoveryRule::clearTemplateObjects($templateids, $hostids);
+ CItem::clearTemplateObjects($templateids, $hostids);
+ CHttpTest::clearTemplateObjects($templateids, $hostids);
}
-
- if ($upd_httptests) {
- if ($clear) {
- $result = API::HttpTest()->delete(array_keys($upd_httptests), true);
- if (!$result) {
- self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear Web scenarios.'));
- }
- }
- else {
- DB::update('httptest', $upd_httptests);
- }
+ else {
+ CDiscoveryRule::unlinkTemplateObjects($templateids, $hostids);
+ CItem::unlinkTemplateObjects($templateids, $hostids);
+ CHttpTest::unlinkTemplateObjects($templateids, $hostids);
}
}
@@ -819,11 +615,11 @@ abstract class CHostGeneral extends CHostBase {
Manager::HttpTest()->link($templateid, $hostids);
}
- API::Item()->syncTemplates($link_request);
+ CItem::linkTemplateObjects($templateids, $hostids);
$ruleids = API::DiscoveryRule()->syncTemplates($templateids, $hostids);
if ($ruleids) {
- API::ItemPrototype()->syncTemplates($link_request);
+ CItemPrototype::linkTemplateObjects($templateids, $hostids);
API::HostPrototype()->syncTemplates($ruleids, $hostids);
}
@@ -1052,11 +848,11 @@ abstract class CHostGeneral extends CHostBase {
}
foreach ($link_requests as $link_request) {
- API::Item()->syncTemplates($link_request);
+ CItem::linkTemplateObjects($link_request['templateids'], $link_request['hostids']);
$ruleids = API::DiscoveryRule()->syncTemplates($link_request['templateids'], $link_request['hostids']);
if ($ruleids) {
- API::ItemPrototype()->syncTemplates($link_request);
+ CItemPrototype::linkTemplateObjects($link_request['templateids'], $link_request['hostids']);
API::HostPrototype()->syncTemplates($ruleids, $link_request['hostids']);
}
}
@@ -1280,186 +1076,15 @@ abstract class CHostGeneral extends CHostBase {
}
/* }}} GRAPHS */
- /* ITEMS, DISCOVERY RULES {{{ */
- $upd_items = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
- $item_prototypeids = [];
-
- $sqlFrom = ' items i1,items i2,hosts h';
- $sqlWhere = ' i2.itemid=i1.templateid'.
- ' AND '.dbConditionInt('i2.hostid', $templateids).
- ' AND '.dbConditionInt('i1.flags', $flags).
- ' AND h.hostid=i1.hostid';
-
- if (!is_null($targetids)) {
- $sqlWhere .= ' AND '.dbConditionInt('i1.hostid', $targetids);
- }
- $sql = 'SELECT DISTINCT i1.itemid,i1.flags,h.status as host_status,i1.type,i1.valuemapid'.
- ' FROM '.$sqlFrom.
- ' WHERE '.$sqlWhere;
-
- $dbItems = DBSelect($sql);
-
- while ($item = DBfetch($dbItems)) {
- if ($clear) {
- $upd_items[$item['flags']][$item['itemid']] = true;
- }
- else {
- $upd_item = ['templateid' => 0];
- if ($item['host_status'] == HOST_STATUS_TEMPLATE && $item['type'] != ITEM_TYPE_HTTPTEST) {
- $upd_item['uuid'] = generateUuidV4();
- }
-
- if ($item['valuemapid'] !== '0') {
- $upd_item['valuemapid'] = '0';
-
- if (in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
- && $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $item['itemid'];
- }
- }
-
- $upd_items[$item['flags']][$item['itemid']] = [
- 'values' => $upd_item,
- 'where' => ['itemid' => $item['itemid']]
- ];
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- if ($clear) {
- CDiscoveryRuleManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_RULE]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]) {
- if ($clear) {
- CItemManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_NORMAL]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- if ($clear) {
- CItemPrototypeManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
-
- if ($item_prototypeids) {
- $options = [
- 'output' => ['itemid'],
- 'filter' => ['parent_itemid' => $item_prototypeids],
- 'sortfield' => ['itemid'],
- 'sortorder' => [ZBX_SORT_DOWN]
- ];
- $result = DBselect(DB::makeSql('item_discovery', $options));
-
- $upd_discovered_items = [];
-
- while ($row = DBfetch($result)) {
- $upd_discovered_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
- }
-
- if ($upd_discovered_items) {
- DB::update('items', $upd_discovered_items);
- }
- }
- }
- }
- /* }}} ITEMS, DISCOVERY RULES */
-
- // host prototypes
- // we need only to unlink host prototypes. in case of unlink and clear they will be deleted together with LLD rules.
- if (!$clear && $upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- $host_prototypes = DBSelect(
- 'SELECT DISTINCT h.hostid,h3.status as host_status'.
- ' FROM hosts h'.
- ' INNER JOIN host_discovery hd ON h.hostid=hd.hostid'.
- ' INNER JOIN hosts h2 ON h.templateid=h2.hostid'.
- ' INNER JOIN host_discovery hd2 ON h.hostid=hd.hostid'.
- ' INNER JOIN items i ON hd.parent_itemid=i.itemid'.
- ' INNER JOIN hosts h3 ON i.hostid=h3.hostid'.
- ' WHERE '.dbConditionInt('hd.parent_itemid', array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]))
- );
-
- $upd_host_prototypes = [];
-
- while ($host_prototype = DBfetch($host_prototypes)) {
- $upd_host_prototype = ['templateid' => 0];
- if ($host_prototype['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_host_prototype['uuid'] = generateUuidV4();
- }
-
- $upd_host_prototypes[$host_prototype['hostid']] = [
- 'values' => $upd_host_prototype,
- 'where' => ['hostid' => $host_prototype['hostid']]
- ];
- }
-
- if ($upd_host_prototypes) {
- DB::update('hosts', $upd_host_prototypes);
- DB::update('group_prototype', [
- 'values' => ['templateid' => 0],
- 'where' => ['hostid' => array_keys($upd_host_prototypes)]
- ]);
- }
- }
-
- // http tests
- $upd_httptests = [];
-
- $sqlWhere = '';
- if (!is_null($targetids)) {
- $sqlWhere = ' AND '.dbConditionInt('ht1.hostid', $targetids);
+ if ($clear) {
+ CDiscoveryRule::clearTemplateObjects($templateids, $targetids);
+ CItem::clearTemplateObjects($templateids, $targetids);
+ CHttpTest::clearTemplateObjects($templateids, $targetids);
}
- $sql = 'SELECT DISTINCT ht1.httptestid,h.status as host_status'.
- ' FROM httptest ht1,httptest ht2,hosts h'.
- ' WHERE ht1.templateid=ht2.httptestid'.
- ' AND ht1.hostid=h.hostid'.
- ' AND '.dbConditionInt('ht2.hostid', $templateids).
- $sqlWhere;
-
- $httptests = DBSelect($sql);
-
- while ($httptest = DBfetch($httptests)) {
- if ($clear) {
- $upd_httptests[$httptest['httptestid']] = true;
- }
- else {
- $upd_httptest = ['templateid' => 0];
- if ($httptest['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_httptest['uuid'] = generateUuidV4();
- }
-
- $upd_httptests[$httptest['httptestid']] = [
- 'values' => $upd_httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ];
- }
- }
-
- if ($upd_httptests) {
- if ($clear) {
- $result = API::HttpTest()->delete(array_keys($upd_httptests), true);
- if (!$result) {
- self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear Web scenarios.'));
- }
- }
- else {
- DB::update('httptest', $upd_httptests);
- }
+ else {
+ CDiscoveryRule::unlinkTemplateObjects($templateids, $targetids);
+ CItem::unlinkTemplateObjects($templateids, $targetids);
+ CHttpTest::unlinkTemplateObjects($templateids, $targetids);
}
parent::unlink($templateids, $targetids);
diff --git a/ui/include/classes/api/services/CHostPrototype.php b/ui/include/classes/api/services/CHostPrototype.php
index 24b862c5471..b1b28d2b250 100644
--- a/ui/include/classes/api/services/CHostPrototype.php
+++ b/ui/include/classes/api/services/CHostPrototype.php
@@ -441,7 +441,7 @@ class CHostPrototype extends CHostBase {
$this->validateCreate($host_prototypes);
$this->createForce($host_prototypes);
- [$tpl_host_prototypes] = $this->getTemplatedObjects($host_prototypes);
+ [$tpl_host_prototypes] = self::getTemplatedObjects($host_prototypes);
if ($tpl_host_prototypes) {
$this->inherit($tpl_host_prototypes);
@@ -547,7 +547,7 @@ class CHostPrototype extends CHostBase {
$this->updateForce($host_prototypes, $db_host_prototypes);
[$tpl_host_prototypes, $tpl_db_host_prototypes] =
- $this->getTemplatedObjects($host_prototypes, $db_host_prototypes);
+ self::getTemplatedObjects($host_prototypes, $db_host_prototypes);
if ($tpl_host_prototypes) {
$this->inherit($tpl_host_prototypes, $tpl_db_host_prototypes);
@@ -730,28 +730,28 @@ class CHostPrototype extends CHostBase {
['else' => true, 'type' => API_UNEXPECTED]
]],
'authprotocol' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& in_array($data['securitylevel'], [ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV, ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV]);
}, 'type' => API_INT32, 'in' => implode(',', array_keys(getSnmpV3AuthProtocols()))],
['else' => true, 'type' => API_UNEXPECTED]
]],
'authpassphrase' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& in_array($data['securitylevel'], [ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV, ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV]);
}, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('interface_snmp', 'authpassphrase')],
['else' => true, 'type' => API_UNEXPECTED]
]],
'privprotocol' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& $data['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV;
}, 'type' => API_INT32, 'in' => implode(',', array_keys(getSnmpV3PrivProtocols()))],
['else' => true, 'type' => API_UNEXPECTED]
]],
'privpassphrase' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& $data['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV;
}, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('interface_snmp', 'privpassphrase')],
@@ -769,7 +769,7 @@ class CHostPrototype extends CHostBase {
* @param array $host_prototypes
* @param array $db_host_prototypes
*/
- protected function updateForce(array &$host_prototypes, array $db_host_prototypes): void {
+ public function updateForce(array &$host_prototypes, array $db_host_prototypes): void {
$upd_host_prototypes = [];
// save the host prototypes
@@ -919,7 +919,7 @@ class CHostPrototype extends CHostBase {
$options = [
'output' => ['group_prototypeid', 'hostid', 'name', 'templateid'],
- 'filter' => ['hostid' => $hostids, 'groupid' => '0']
+ 'filter' => ['hostid' => $hostids, 'groupid' => 0]
];
$db_groups = DBselect(DB::makeSql('group_prototype', $options));
@@ -1647,6 +1647,46 @@ class CHostPrototype extends CHostBase {
}
/**
+ * @param array $ruleids
+ */
+ public static function unlinkTemplateObjects(array $ruleids): void {
+ $result = DBselect(
+ 'SELECT hd.hostid,h.status AS host_status'.
+ ' FROM host_discovery hd,items i,hosts h'.
+ ' WHERE hd.parent_itemid=i.itemid'.
+ ' AND i.hostid=h.hostid'.
+ ' AND '.dbConditionId('hd.parent_itemid', $ruleids)
+ );
+
+ $upd_host_prototypes = [];
+ $hostids = [];
+
+ while ($row = DBfetch($result)) {
+ $upd_host_prototype = ['templateid' => 0];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $upd_host_prototype += ['uuid' => generateUuidV4()];
+ }
+
+ $upd_host_prototypes[$row['hostid']] = [
+ 'values' => $upd_host_prototype,
+ 'where' => ['hostid' => $row['hostid']]
+ ];
+
+ $hostids[] = $row['hostid'];
+ }
+
+ if ($upd_host_prototypes) {
+ DB::update('hosts', $upd_host_prototypes);
+
+ DB::update('group_prototype', [
+ 'values' => ['templateid' => 0],
+ 'where' => ['hostid' => $hostids]
+ ]);
+ }
+ }
+
+ /**
* Updates the children of the host prototypes on the given hosts and propagates the inheritance to the child hosts.
*
* @param array $host_prototypes
@@ -1697,7 +1737,7 @@ class CHostPrototype extends CHostBase {
$this->createForce($ins_host_prototypes, true);
}
- [$tpl_host_prototypes, $tpl_db_host_prototypes] = $this->getTemplatedObjects(
+ [$tpl_host_prototypes, $tpl_db_host_prototypes] = self::getTemplatedObjects(
array_merge($upd_host_prototypes, $ins_host_prototypes), $upd_db_host_prototypes
);
diff --git a/ui/include/classes/api/services/CHttpTest.php b/ui/include/classes/api/services/CHttpTest.php
index 590695af7c2..ae43d1b3bab 100644
--- a/ui/include/classes/api/services/CHttpTest.php
+++ b/ui/include/classes/api/services/CHttpTest.php
@@ -345,7 +345,10 @@ class CHttpTest extends CApiService {
}
$this->checkAndAddUuid($httptests);
- $this->checkHostPermissions(array_keys($names_by_hostid));
+
+ $this->checkHostsAndTemplates($httptests, $db_hosts, $db_templates);
+ self::addHostStatus($httptests, $db_hosts, $db_templates);
+
$this->checkDuplicates($names_by_hostid);
$this->validateAuthParameters($httptests, __FUNCTION__);
$this->validateSslParameters($httptests, __FUNCTION__);
@@ -573,17 +576,25 @@ class CHttpTest extends CApiService {
}
/**
- * Delete web scenario.
- *
* @param array $httptestids
- * @param bool $nopermissions
*
* @return array
*/
- public function delete(array $httptestids, $nopermissions = false) {
- // TODO: remove $nopermissions hack
+ public function delete(array $httptestids) {
+ $this->validateDelete($httptestids, $db_httptests);
+ self::deleteForce($db_httptests);
+
+ return ['httptestids' => $httptestids];
+ }
+
+ /**
+ * @param array $httptestids
+ * @param array|null $db_httptests
+ */
+ private function validateDelete(array $httptestids, ?array &$db_httptests): void {
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
+
if (!CApiInputValidator::validate($api_input_rules, $httptestids, '/', $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
@@ -595,24 +606,23 @@ class CHttpTest extends CApiService {
'preservekeys' => true
]);
- if (!$nopermissions) {
- foreach ($httptestids as $httptestid) {
- if (!array_key_exists($httptestid, $db_httptests)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
- }
-
- $db_httptest = $db_httptests[$httptestid];
+ if (count($db_httptests) != count($httptestids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
- if ($db_httptest['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Cannot delete templated web scenario "%1$s".', $db_httptest['name'])
- );
- }
+ foreach ($httptestids as $httptestid) {
+ if ($db_httptests[$httptestid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Cannot delete templated web scenario "%1$s".', $db_httptests[$httptestid]['name'])
+ );
}
}
+ }
+ /**
+ * @param array $db_httptests
+ */
+ public static function deleteForce(array $db_httptests): void {
self::addInheritedHttptests($db_httptests);
$del_httptestids = array_keys($db_httptests);
@@ -628,12 +638,12 @@ class CHttpTest extends CApiService {
]);
DB::delete('httptest', ['httptestid' => $del_httptestids]);
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_SCENARIO, $db_httptests);
-
- return ['httptestids' => $httptestids];
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_SCENARIO, $db_httptests);
}
/**
+ * Add the inherited web scenarios of the given web scenarios to the given web scenario array.
+ *
* @param array $db_httptests
*/
private static function addInheritedHttptests(array &$db_httptests): void {
@@ -659,20 +669,26 @@ class CHttpTest extends CApiService {
}
/**
+ * Delete items, which would remain without web scenarios after the given web scenarios deletion.
+ *
* @param array $del_httptestids
*/
private static function deleteAffectedItems(array $del_httptestids): void {
$db_items = DBfetchArrayAssoc(DBselect(
- 'SELECT hti.itemid'.
+ 'SELECT hti.itemid,i.name'.
' FROM httptestitem hti,items i'.
' WHERE hti.itemid=i.itemid'.
' AND '.dbConditionId('hti.httptestid', $del_httptestids)
), 'itemid');
- CItemManager::delete(array_keys($db_items));
+ CItem::addInheritedItems($db_items);
+ DB::delete('httptestitem', ['itemid' => array_keys($db_items)]);
+ CItem::deleteForce($db_items);
}
/**
+ * Delete steps of the given web scenarios.
+ *
* @param array $del_httptestids
*/
private static function deleteAffectedSteps(array $del_httptestids): void {
@@ -688,50 +704,77 @@ class CHttpTest extends CApiService {
}
/**
+ * Delete items of the given web scenario steps.
+ *
* @param array $del_stepids
*/
- public static function deleteAffectedStepItems(array $del_stepids): void {
+ private static function deleteAffectedStepItems(array $del_stepids): void {
$db_items = DBfetchArrayAssoc(DBselect(
- 'SELECT hsi.itemid'.
+ 'SELECT hsi.itemid,i.name'.
' FROM httpstepitem hsi,items i'.
' WHERE hsi.itemid=i.itemid'.
' AND '.dbConditionId('hsi.httpstepid', $del_stepids)
), 'itemid');
- CItemManager::delete(array_keys($db_items));
+ CItem::addInheritedItems($db_items);
+ DB::delete('httpstepitem', ['itemid' => array_keys($db_items)]);
+ CItem::deleteForce($db_items);
}
/**
- * Checks if the current user has access to the given hosts and templates.
+ * Check that host IDs of given web scenarios are valid.
+ * If host IDs are valid, $db_hosts and $db_templates parameters will be filled with found hosts and templates.
*
- * @param array $hostids an array of host or template IDs
+ * @param array $httptests
+ * @param array|null $db_hosts
+ * @param array|null $db_templates
*
- * @throws APIException if the user doesn't have write permissions for the given hosts.
+ * @throws APIException
*/
- private function checkHostPermissions(array $hostids) {
- if ($hostids) {
- $count = API::Host()->get([
- 'countOutput' => true,
- 'hostids' => $hostids,
- 'editable' => true
- ]);
+ protected static function checkHostsAndTemplates(array $httptests, array &$db_hosts = null,
+ array &$db_templates = null): void {
+ $hostids = array_unique(array_column($httptests, 'hostid'));
- if ($count == count($hostids)) {
- return;
- }
+ $db_templates = API::Template()->get([
+ 'output' => [],
+ 'templateids' => $hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
- $count += API::Template()->get([
- 'countOutput' => true,
- 'templateids' => $hostids,
- 'editable' => true
- ]);
+ $_hostids = array_diff($hostids, array_keys($db_templates));
- if ($count != count($hostids)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ $db_hosts = $_hostids
+ ? API::Host()->get([
+ 'output' => ['status'],
+ 'hostids' => $_hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ])
+ : [];
+
+ if (count($db_templates) + count($db_hosts) != count($hostids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
+ }
+
+ /**
+ * Add host_status property to given web scenarios in accordance of given hosts and templates statuses.
+ *
+ * @param array $httptests
+ * @param array $db_hosts
+ * @param array $db_templates
+ */
+ protected static function addHostStatus(array &$httptests, array $db_hosts, array $db_templates): void {
+ foreach ($httptests as &$httptest) {
+ if (array_key_exists($httptest['hostid'], $db_templates)) {
+ $httptest['host_status'] = HOST_STATUS_TEMPLATE;
+ }
+ else {
+ $httptest['host_status'] = $db_hosts[$httptest['hostid']]['status'];
}
}
+ unset($httptest);
}
/**
@@ -774,23 +817,27 @@ class CHttpTest extends CApiService {
$db_httptest = $db_httptests[$httptest['httptestid']];
- if ($db_httptest['templateid'] != 0) {
- if (count($httptest['steps']) != count($db_httptest['steps'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect templated web scenario step count.'));
- }
+ if ($db_httptest['templateid'] != 0 && count($httptest['steps']) != count($db_httptest['steps'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect templated web scenario step count.'));
+ }
- foreach ($httptest['steps'] as $httpstep) {
- if (!array_key_exists('httpstepid', $httpstep)) {
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)) {
+ if ($db_httptest['templateid'] == 0) {
+ continue;
+ }
+ else {
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
'Cannot update step for a templated web scenario "%1$s": %2$s.', $httptest['name'],
_s('the parameter "%1$s" is missing', 'httpstepid')
));
}
- elseif (!array_key_exists($httpstep['httpstepid'], $db_httptest['steps'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('No permissions to referred object or it does not exist!')
- );
- }
+ }
+
+ if (!array_key_exists($step['httpstepid'], $db_httptest['steps'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('No permissions to referred object or it does not exist!')
+ );
}
}
}
@@ -1128,4 +1175,61 @@ class CHttpTest extends CApiService {
}
unset($httptest);
}
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('hht.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT hht.httptestid,hht.name,h.status AS host_status'.
+ ' FROM httptest ht,httptest hht,hosts h'.
+ ' WHERE ht.httptestid=hht.templateid'.
+ ' AND hht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.hostid', $templateids).
+ $hostids_condition
+ );
+
+ $httptests = [];
+
+ while ($row = DBfetch($result)) {
+ $httptest = [
+ 'httptestid' => $row['httptestid'],
+ 'name' => $row['name'],
+ 'templateid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $httptest += ['uuid' => generateUuidV4()];
+ }
+
+ $httptests[] = $httptest;
+ }
+
+ if ($httptests) {
+ Manager::HttpTest()->update($httptests);
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('hht.hostid', $hostids) : '';
+
+ $db_httptests = DBfetchArrayAssoc(DBselect(
+ 'SELECT hht.httptestid,hht.name'.
+ ' FROM httptest ht,httptest hht'.
+ ' WHERE ht.httptestid=hht.templateid'.
+ ' AND '.dbConditionId('ht.hostid', $templateids).
+ $hostids_condition
+ ), 'httptestid');
+
+ if ($db_httptests) {
+ self::deleteForce($db_httptests);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CItem.php b/ui/include/classes/api/services/CItem.php
index 9d18482eebf..1cc6283fadb 100644
--- a/ui/include/classes/api/services/CItem.php
+++ b/ui/include/classes/api/services/CItem.php
@@ -29,40 +29,61 @@ class CItem extends CItemGeneral {
protected $sortColumns = ['itemid', 'name', 'key_', 'delay', 'history', 'trends', 'type', 'status'];
/**
- * Define a set of supported pre-processing rules.
- *
- * @var array
+ * @inheritDoc
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC, ZBX_PREPROC_DELTA_VALUE,
+ ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE,
+ ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON,
+ ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_VALUE,
+ ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT, ZBX_PREPROC_PROMETHEUS_PATTERN,
+ ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE,
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX,
+ ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML,
+ ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
+ ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE
+ ];
+
+ /**
+ * @inheritDoc
*/
- const SUPPORTED_PREPROCESSING_TYPES = [ZBX_PREPROC_REGSUB, ZBX_PREPROC_TRIM, ZBX_PREPROC_RTRIM,
- ZBX_PREPROC_LTRIM, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_DELTA_VALUE,
- ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_REGSUB, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH,
ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
- ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
- ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
];
/**
- * Define a set of supported item types.
- *
- * @var array
+ * @inheritDoc
*/
- const SUPPORTED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
- ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT,
- ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ public const SUPPORTED_ITEM_TYPES = [
+ ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
];
- public function __construct() {
- parent::__construct();
-
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_EXISTS_TEMPLATE => _('Item "%1$s" already exists on "%2$s", inherited from another template.'),
- self::ERROR_EXISTS => _('Item "%1$s" already exists on "%2$s".'),
- self::ERROR_INVALID_KEY => _('Invalid key "%1$s" for item "%2$s" on "%3$s": %4$s.')
- ]);
- }
+ /**
+ * @inheritDoc
+ */
+ protected const VALUE_TYPE_FIELD_NAMES = [
+ ITEM_VALUE_TYPE_FLOAT => ['units', 'trends', 'valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_STR => ['valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_LOG => ['logtimefmt'],
+ ITEM_VALUE_TYPE_UINT64 => ['units', 'trends', 'valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_TEXT => ['inventory_link']
+ ];
/**
* Get items data.
@@ -435,7 +456,7 @@ class CItem extends CItemGeneral {
}
if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersStringToArray($item['headers']);
+ $item['headers'] = self::headersStringToArray($item['headers']);
}
}
unset($item);
@@ -463,589 +484,1071 @@ class CItem extends CItemGeneral {
}
/**
- * Create item.
- *
- * @param $items
+ * @param array $items
*
* @return array
*/
- public function create($items) {
- $items = zbx_toArray($items);
+ public function create(array $items): array {
+ self::validateCreate($items);
- parent::checkInput($items);
- self::validateInventoryLinks($items);
+ self::createForce($items);
+ self::inherit($items);
- foreach ($items as &$item) {
- $item['flags'] = ZBX_FLAG_DISCOVERY_NORMAL;
- unset($item['itemid']);
- }
- unset($item);
-
- $this->validateDependentItems($items);
-
- foreach ($items as &$item) {
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $item)) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
-
- if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ return ['itemids' => array_column($items, 'itemid')];
+ }
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
- }
- else {
- $item['query_fields'] = '';
- $item['headers'] = '';
- }
+ /**
+ * @param array $items
+ *
+ * @throws APIException
+ */
+ protected static function validateCreate(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'fields' => [
+ 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
- }
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
- // Get only hosts not templates from items
- $hosts = API::Host()->get([
- 'output' => [],
- 'hostids' => zbx_objectValues($items, 'hostid'),
- 'preservekeys' => true
- ]);
- foreach ($items as &$item) {
- if (array_key_exists($item['hostid'], $hosts)) {
- $item['rtdata'] = true;
- }
+ self::checkHostsAndTemplates($items, $db_hosts, $db_templates);
+ self::addHostStatus($items, $db_hosts, $db_templates);
+ self::addFlags($items, ZBX_FLAG_DISCOVERY_NORMAL);
+
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'uniq' => [['uuid'], ['hostid', 'key_']], 'fields' => [
+ 'host_status' => ['type' => API_ANY],
+ 'flags' => ['type' => API_ANY],
+ 'uuid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_TEMPLATE])], 'type' => API_UUID],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'uuid')]
+ ]],
+ 'hostid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0', 'default' => 0]
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
- $this->createReal($items);
- $this->inherit($items);
+ self::validateByType(array_keys($api_input_rules['fields']), $items);
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::checkAndAddUuid($items);
+ self::checkDuplicates($items);
+ self::checkValueMaps($items);
+ self::checkInventoryLinks($items);
+ self::checkHostInterfaces($items);
+ self::checkDependentItems($items);
}
/**
- * Create host item.
- *
* @param array $items
*/
- protected function createReal(array &$items) {
- $items_rtdata = [];
+ public static function createForce(array &$items): void {
+ $itemids = DB::insert('items', $items);
- foreach ($items as $key => &$item) {
- if ($item['type'] != ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = null;
- }
+ $ins_items_rtdata = [];
+ $host_statuses = [];
+
+ foreach ($items as &$item) {
+ $item['itemid'] = array_shift($itemids);
- if (array_key_exists('rtdata', $item)) {
- $items_rtdata[$key] = [];
- unset($item['rtdata']);
+ if (in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ $ins_items_rtdata[] = ['itemid' => $item['itemid']];
}
+
+ $host_statuses[] = $item['host_status'];
+ unset($item['host_status']);
}
unset($item);
- $itemids = DB::insert('items', $items);
-
- foreach ($items_rtdata as $key => &$value) {
- $value['itemid'] = $itemids[$key];
+ if ($ins_items_rtdata) {
+ DB::insertBatch('item_rtdata', $ins_items_rtdata, false);
}
- unset($value);
- DB::insert('item_rtdata', $items_rtdata, false);
+ self::updateParameters($items);
+ self::updatePreprocessing($items);
+ self::updateTags($items);
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
- }
+ self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_ITEM, $items);
- $this->createItemParameters($items, $itemids);
- $this->createItemPreprocessing($items);
- $this->createItemTags($items, $itemids);
+ foreach ($items as &$item) {
+ $item['host_status'] = array_shift($host_statuses);
+ }
+ unset($item);
}
/**
- * Update host items.
- *
* @param array $items
+ *
+ * @return array
*/
- protected function updateReal(array $items) {
- CArrayHelper::sort($items, ['itemid']);
+ public function update(array $items): array {
+ $this->validateUpdate($items, $db_items);
- $data = [];
- foreach ($items as $item) {
- unset($item['flags']); // flags cannot be changed
- $data[] = ['values' => $item, 'where' => ['itemid' => $item['itemid']]];
- }
- DB::update('items', $data);
+ $itemids = array_column($items, 'itemid');
+
+ self::updateForce($items, $db_items);
+ self::inherit($items, $db_items);
- $this->updateItemParameters($items);
- $this->updateItemPreprocessing($items);
- $this->updateItemTags($items);
+ return ['itemids' => $itemids];
}
/**
- * Update item.
+ * @param array $items
+ * @param array|null $db_items
*
- * @param array $items
- *
- * @return array
+ * @throws APIException
*/
- public function update($items) {
- $items = zbx_toArray($items);
+ protected function validateUpdate(array &$items, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'uniq' => [['itemid']], 'fields' => [
+ 'itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
- parent::checkInput($items, true);
- self::validateInventoryLinks($items, true);
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
- $db_items = $this->get([
- 'output' => ['flags', 'type', 'master_itemid', 'authtype', 'allow_traps', 'retrieve_mode', 'value_type'],
- 'itemids' => zbx_objectValues($items, 'itemid'),
- 'editable' => true,
- 'preservekeys' => true
+ $count = $this->get([
+ 'countOutput' => true,
+ 'itemids' => array_column($items, 'itemid'),
+ 'editable' => true
]);
- $items = $this->extendFromObjects(zbx_toHash($items, 'itemid'), $db_items, ['flags', 'type', 'authtype',
- 'master_itemid', 'value_type'
- ]);
+ if ($count != count($items)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
- $this->validateDependentItems($items);
-
- $defaults = DB::getDefaults('items');
- $clean = [
- ITEM_TYPE_HTTPAGENT => [
- 'url' => '',
- 'query_fields' => '',
- 'timeout' => $defaults['timeout'],
- 'status_codes' => $defaults['status_codes'],
- 'follow_redirects' => $defaults['follow_redirects'],
- 'request_method' => $defaults['request_method'],
- 'allow_traps' => $defaults['allow_traps'],
- 'post_type' => $defaults['post_type'],
- 'http_proxy' => '',
- 'headers' => '',
- 'retrieve_mode' => $defaults['retrieve_mode'],
- 'output_format' => $defaults['output_format'],
- 'ssl_key_password' => '',
- 'verify_peer' => $defaults['verify_peer'],
- 'verify_host' => $defaults['verify_host'],
- 'ssl_cert_file' => '',
- 'ssl_key_file' => '',
- 'posts' => ''
- ]
- ];
+ /*
+ * The fields "headers" and "query_fields" in API are arrays, but there is necessary to get the values of these
+ * fields as they stored in database.
+ */
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_column($items, 'itemid'),
+ 'preservekeys' => true
+ ]);
- foreach ($items as &$item) {
- $type_change = ($item['type'] != $db_items[$item['itemid']]['type']);
+ self::addInternalFields($db_items);
- if ($item['type'] != ITEM_TYPE_DEPENDENT && $db_items[$item['itemid']]['master_itemid'] != 0) {
- $item['master_itemid'] = 0;
- }
+ foreach ($items as $i => &$item) {
+ $db_item = $db_items[$item['itemid']];
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = array_merge($item, $clean[ITEM_TYPE_HTTPAGENT]);
+ if ($db_item['templateid'] != 0) {
+ $api_input_rules = ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
- if ($item['type'] != ITEM_TYPE_SSH) {
- $item['authtype'] = $defaults['authtype'];
- $item['username'] = '';
- $item['password'] = '';
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- if ($item['type'] != ITEM_TYPE_TRAPPER) {
- $item['trapper_hosts'] = '';
- }
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getInheritedValidationRules();
}
+ elseif ($db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
+ $api_input_rules = self::getDiscoveredValidationRules();
+ }
+ else {
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- // Clean username and password when authtype is set to HTTPTEST_AUTH_NONE.
- if ($item['authtype'] == HTTPTEST_AUTH_NONE) {
- $item['username'] = '';
- $item['password'] = '';
- }
+ $api_input_rules = self::getValidationRules();
+ }
- if (array_key_exists('allow_traps', $item) && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF
- && $item['allow_traps'] != $db_items[$item['itemid']]['allow_traps']) {
- $item['trapper_hosts'] = '';
- }
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+ unset($item);
- if (array_key_exists('query_fields', $item) && is_array($item['query_fields'])) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['type', 'key_']);
- if (array_key_exists('headers', $item) && is_array($item['headers'])) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ self::validateByType(array_keys($api_input_rules['fields']), $items, $db_items);
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)
- && $db_items[$item['itemid']]['retrieve_mode'] != HTTPTEST_STEP_RETRIEVE_MODE_HEADERS) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
- }
- else {
- $item['query_fields'] = '';
- $item['headers'] = '';
- }
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['hostid', 'host_status', 'flags']);
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT) {
- if ($item['type'] != ITEM_TYPE_SSH && $item['type'] != ITEM_TYPE_DB_MONITOR
- && $item['type'] != ITEM_TYPE_TELNET && $item['type'] != ITEM_TYPE_CALCULATED) {
- $item['params'] = '';
- }
+ self::validateUniqueness($items);
- if ($item['type'] != ITEM_TYPE_HTTPAGENT) {
- $item['timeout'] = $defaults['timeout'];
- }
- }
+ self::addAffectedObjects($items, $db_items);
+
+ self::checkDuplicates($items, $db_items);
+ self::checkValueMaps($items, $db_items);
+ self::checkInventoryLinks($items, $db_items);
+ self::checkHostInterfaces($items, $db_items);
+ self::checkDependentItems($items, $db_items);
+ }
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- if ($item['value_type'] != $db_items[$item['itemid']]['value_type']) {
- // Reset valuemapid when value_type is LOG or TEXT.
- $item['valuemapid'] = 0;
+ /**
+ * @return array
+ */
+ private static function getValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => ITEM_NO_STORAGE_VALUE.','.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getInheritedValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'value_type' => ['type' => API_ANY],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => ITEM_NO_STORAGE_VALUE.','.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getDiscoveredValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'history' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'trends' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'inventory_link' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'description' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED]
+ ]];
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ public static function updateForce(array &$items, array &$db_items): void {
+ // Helps to avoid deadlocks.
+ CArrayHelper::sort($items, ['itemid'], ZBX_SORT_DOWN);
+
+ self::addFieldDefaultsByType($items, $db_items);
+
+ $upd_items = [];
+ $upd_itemids = [];
+
+ $internal_fields = array_flip(['itemid', 'type', 'key_', 'value_type', 'hostid', 'flags', 'host_status']);
+ $nested_object_fields = array_flip(['tags', 'preprocessing', 'parameters']);
+
+ foreach ($items as $i => &$item) {
+ $upd_item = DB::getUpdatedValues('items', $item, $db_items[$item['itemid']]);
+
+ if ($upd_item) {
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $item['itemid']]
+ ];
+
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item = array_intersect_key($item,
+ array_flip(['authtype']) + $internal_fields + $upd_item + $nested_object_fields
+ );
}
else {
- unset($item['valuemapid']);
+ $item = array_intersect_key($item, $internal_fields + $upd_item + $nested_object_fields);
}
- }
- if (array_key_exists('tags', $item)) {
- $item['tags'] = array_map(function ($tag) {
- return $tag + ['value' => ''];
- }, $item['tags']);
+ $upd_itemids[$i] = $item['itemid'];
}
-
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ else {
+ $item = array_intersect_key($item, $internal_fields + $nested_object_fields);
}
}
unset($item);
- $this->updateReal($items);
- $this->inherit($items);
+ if ($upd_items) {
+ DB::update('items', $upd_items);
+ }
+
+ self::updateTags($items, $db_items, $upd_itemids);
+ self::updatePreprocessing($items, $db_items, $upd_itemids);
+ self::updateParameters($items, $db_items, $upd_itemids);
+
+ $items = array_intersect_key($items, $upd_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($upd_itemids));
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_ITEM, $items, $db_items);
}
/**
- * Delete items.
- *
* @param array $itemids
*
+ * @throws APIException
+ *
* @return array
*/
- public function delete(array $itemids) {
+ public function delete(array $itemids): array {
$this->validateDelete($itemids, $db_items);
- CItemManager::delete($itemids);
-
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM, $db_items);
+ self::deleteForce($db_items);
return ['itemids' => $itemids];
}
/**
- * Validates the input parameters for the delete() method.
+ * @param array $itemids
+ * @param array|null $db_items
*
- * @param array $itemids [IN/OUT]
- * @param array $db_items [OUT]
- *
- * @throws APIException if the input is invalid.
+ * @throws APIException
*/
- private function validateDelete(array &$itemids, array &$db_items = null) {
+ private function validateDelete(array $itemids, ?array &$db_items): void {
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
+
if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
$db_items = $this->get([
- 'output' => ['itemid', 'name', 'templateid', 'flags'],
+ 'output' => ['itemid', 'name', 'templateid'],
'itemids' => $itemids,
'editable' => true,
'preservekeys' => true
]);
- foreach ($itemids as $itemid) {
- if (!array_key_exists($itemid, $db_items)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ if (count($db_items) != count($itemids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ foreach ($itemids as $i => $itemid) {
+ if ($db_items[$itemid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('cannot delete inherited item')
+ ));
}
+ }
+ }
- $db_item = $db_items[$itemid];
+ /**
+ * @param array $templateids
+ * @param array $hostids
+ */
+ public static function linkTemplateObjects(array $templateids, array $hostids): void {
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['interfaceid', 'parameters'])),
+ 'filter' => [
+ 'hostid' => $templateids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => self::SUPPORTED_ITEM_TYPES
+ ],
+ 'preservekeys' => true
+ ]);
- if ($db_item['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete templated item.'));
+ if (!$db_items) {
+ return;
+ }
+
+ self::addInternalFields($db_items);
+
+ $items = [];
+
+ foreach ($db_items as $db_item) {
+ $item = array_intersect_key($db_item, array_flip(['itemid', 'type']));
+
+ if ($db_item['type'] == ITEM_TYPE_SCRIPT) {
+ $item += ['parameters' => []];
}
+
+ $items[] = $item + [
+ 'preprocessing' => [],
+ 'tags' => []
+ ];
}
+
+ self::addAffectedObjects($items, $db_items);
+
+ $items = array_values($db_items);
+
+ foreach ($items as &$item) {
+ if (array_key_exists('parameters', $item)) {
+ $item['parameters'] = array_values($item['parameters']);
+ }
+
+ $item['preprocessing'] = array_values($item['preprocessing']);
+ $item['tags'] = array_values($item['tags']);
+ }
+ unset($item);
+
+ self::inherit($items, [], $hostids);
}
- public function syncTemplates($data) {
- $data['templateids'] = zbx_toArray($data['templateids']);
- $data['hostids'] = zbx_toArray($data['hostids']);
+ /**
+ * @inheritDoc
+ */
+ protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void {
+ $tpl_links = self::getTemplateLinks($items, $hostids);
+
+ if ($hostids === null) {
+ self::filterObjectsToInherit($items, $db_items, $tpl_links);
- $output = [];
- foreach ($this->fieldRules as $field_name => $rules) {
- if (!array_key_exists('system', $rules) && !array_key_exists('host', $rules)) {
- $output[] = $field_name;
+ if (!$items) {
+ return;
}
}
- $tpl_items = $this->get([
- 'output' => $output,
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'hostids' => $data['templateids'],
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true,
- 'nopermissions' => true
- ]);
+ self::checkDoubleInheritedNames($items, $db_items, $tpl_links);
- foreach ($tpl_items as &$tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $tpl_item) && is_array($tpl_item['query_fields'])) {
- $tpl_item['query_fields'] = $tpl_item['query_fields']
- ? json_encode($tpl_item['query_fields'])
- : '';
- }
+ if ($hostids !== null && !$is_dep_items) {
+ $dep_items_to_link = [];
+
+ $item_indexes = array_flip(array_column($items, 'itemid'));
- if (array_key_exists('headers', $tpl_item) && is_array($tpl_item['headers'])) {
- $tpl_item['headers'] = $this->headersArrayToString($tpl_item['headers']);
+ foreach ($items as $i => $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dep_items_to_link[$item_indexes[$item['master_itemid']]][$i] = $item;
+
+ unset($items[$i]);
}
}
+ }
+
+ $chunks = self::getInheritChunks($items, $tpl_links);
+
+ foreach ($chunks as $chunk) {
+ $_items = array_intersect_key($items, array_flip($chunk['item_indexes']));
+ $_db_items = array_intersect_key($db_items, array_flip(array_column($_items, 'itemid')));
+ $_hostids = array_keys($chunk['hosts']);
+
+ self::inheritChunk($_items, $_db_items, $tpl_links, $_hostids);
+ }
+
+ if ($hostids !== null && !$is_dep_items) {
+ self::inheritDependentItems($dep_items_to_link, $items, $hostids);
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ */
+ protected static function inheritChunk(array $items, array $db_items, array $tpl_links, array $hostids): void {
+ $items_to_link = [];
+ $items_to_update = [];
+
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ $items_to_link[] = $item;
+ }
else {
- $tpl_item['query_fields'] = '';
- $tpl_item['headers'] = '';
+ $items_to_update[] = $item;
+ }
+
+ unset($items[$i]);
+ }
+
+ $ins_items = [];
+ $upd_items = [];
+ $upd_db_items = [];
+
+ if ($items_to_link) {
+ $upd_db_items = self::getChildObjectsUsingName($items_to_link, $hostids);
+
+ if ($upd_db_items) {
+ $upd_items = self::getUpdChildObjectsUsingName($items_to_link, $upd_db_items);
}
+
+ $ins_items = self::getInsChildObjects($items_to_link, $upd_db_items, $tpl_links, $hostids);
}
- unset($tpl_item);
- $this->inherit($tpl_items, $data['hostids']);
+ if ($items_to_update) {
+ $_upd_db_items = self::getChildObjectsUsingTemplateid($items_to_update, $db_items, $hostids);
+ $_upd_items = self::getUpdChildObjectsUsingTemplateid($items_to_update, $db_items, $_upd_db_items);
+
+ self::checkDuplicates($_upd_items, $_upd_db_items);
+
+ $upd_items = array_merge($upd_items, $_upd_items);
+ $upd_db_items += $_upd_db_items;
+ }
+
+ self::setChildMasterItemIds($upd_items, $ins_items, $hostids);
+
+ $edit_items = array_merge($upd_items, $ins_items);
+
+ self::checkDependentItems($edit_items, $upd_db_items, true);
+ self::checkInventoryLinks($edit_items, $upd_db_items, true);
- return true;
+ self::addInterfaceIds($upd_items, $upd_db_items, $ins_items);
+
+ if ($upd_items) {
+ self::updateForce($upd_items, $upd_db_items);
+ }
+
+ if ($ins_items) {
+ self::createForce($ins_items);
+ }
+
+ self::inherit(array_merge($upd_items, $ins_items), $upd_db_items);
}
/**
- * Check item specific fields:
- * - validate history and trends using simple interval parser and user macro parser;
- * - validate item preprocessing.
- *
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
+ * @param array $db_items
+ * @param array $hostids
*
- * @throws APIException if the input is invalid.
+ * @return array
*/
- protected function checkSpecificFields(array $item, $method) {
- if (array_key_exists('history', $item)
- && !validateTimeUnit($item['history'], SEC_PER_HOUR, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'history', $error)
- );
- }
+ private static function getChildObjectsUsingTemplateid(array $items, array $db_items, array $hostids): array {
+ $upd_db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'filter' => [
+ 'templateid' => array_keys($db_items),
+ 'hostid' => $hostids
+ ],
+ 'preservekeys' => true
+ ]);
- if (array_key_exists('trends', $item)
- && !validateTimeUnit($item['trends'], SEC_PER_DAY, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trends', $error)
- );
+ self::addInternalFields($upd_db_items);
+
+ if ($upd_db_items) {
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+ $db_item = $db_items[$upd_db_item['templateid']];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type']
+ ];
+
+ $upd_item += array_intersect_key([
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ], $db_item);
+
+ $upd_items[] = $upd_item;
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
}
+
+ return $upd_db_items;
}
/**
- * Check, if items that are about to be inserted or updated violate the rule:
- * only one item can be linked to a inventory filed.
- * If everything is ok, function return true or throws Exception otherwise
- *
- * @static
- *
* @param array $items
- * @param bool $update whether this is update operation
+ * @param array $db_items
+ * @param array $upd_db_items
*
- * @return bool
+ * @return array
*/
- public static function validateInventoryLinks(array $items, $update = false) {
- // inventory link field is not being updated, or being updated to 0, no need to validate anything then
- foreach ($items as $i => $item) {
- if (!isset($item['inventory_link']) || $item['inventory_link'] == 0) {
- unset($items[$i]);
+ private static function getUpdChildObjectsUsingTemplateid(array $items, array $db_items,
+ array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ continue;
}
- }
- if (zbx_empty($items)) {
- return true;
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['itemid']] = $i;
}
+ unset($item);
- $possibleHostInventories = getHostInventories();
- if ($update) {
- // for successful validation we need three fields for each item: inventory_link, hostid and key_
- // problem is, that when we are updating an item, we might not have them, because they are not changed
- // so, we need to find out what is missing and use API to get the lacking info
- $itemsWithNoHostId = [];
- $itemsWithNoInventoryLink = [];
- $itemsWithNoKeys = [];
- foreach ($items as $item) {
- if (!isset($item['inventory_link'])) {
- $itemsWithNoInventoryLink[$item['itemid']] = $item['itemid'];
- }
- if (!isset($item['hostid'])) {
- $itemsWithNoHostId[$item['itemid']] = $item['itemid'];
- }
- if (!isset($item['key_'])) {
- $itemsWithNoKeys[$item['itemid']] = $item['itemid'];
- }
- }
- $itemsToFind = array_merge($itemsWithNoHostId, $itemsWithNoInventoryLink, $itemsWithNoKeys);
+ $upd_items = [];
- // are there any items with lacking info?
- if (!zbx_empty($itemsToFind)) {
- $missingInfo = API::Item()->get([
- 'output' => ['hostid', 'inventory_link', 'key_'],
- 'filter' => ['itemid' => $itemsToFind],
- 'nopermissions' => true
- ]);
- $missingInfo = zbx_toHash($missingInfo, 'itemid');
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
- // appending host ids, inventory_links and keys where they are needed
- foreach ($items as $i => $item) {
- if (isset($missingInfo[$item['itemid']])) {
- if (!isset($items[$i]['hostid'])) {
- $items[$i]['hostid'] = $missingInfo[$item['itemid']]['hostid'];
- }
- if (!isset($items[$i]['inventory_link'])) {
- $items[$i]['inventory_link'] = $missingInfo[$item['itemid']]['inventory_link'];
- }
- if (!isset($items[$i]['key_'])) {
- $items[$i]['key_'] = $missingInfo[$item['itemid']]['key_'];
- }
+ $upd_items[] = array_intersect_key($upd_db_item,
+ array_flip(['itemid', 'hostid', 'templateid', 'host_status'])
+ ) + $item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getChildObjectsUsingName(array $items, array $hostids): array {
+ $result = DBselect(
+ 'SELECT i.itemid,ht.hostid,i.key_,i.templateid,i.flags,h.status AS host_status,'.
+ 'ht.templateid AS parent_hostid'.
+ ' FROM hosts_templates ht,items i,hosts h'.
+ ' WHERE ht.hostid=i.hostid'.
+ ' AND ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_unique(array_column($items, 'hostid'))).
+ ' AND '.dbConditionString('i.key_', array_unique(array_column($items, 'key_'))).
+ ' AND '.dbConditionId('ht.hostid', $hostids)
+ );
+
+ $upd_db_items = [];
+ $parent_indexes = [];
+
+ while ($row = DBfetch($result)) {
+ foreach ($items as $i => $item) {
+ if (bccomp($row['parent_hostid'], $item['hostid']) == 0 && $row['key_'] === $item['key_']) {
+ if ($row['flags'] == $item['flags'] && $row['templateid'] == 0) {
+ $upd_db_items[$row['itemid']] = $row;
+ $parent_indexes[$row['itemid']] = $i;
+ }
+ else {
+ self::showObjectMismatchError($item, $row);
}
}
}
}
- $hostids = zbx_objectValues($items, 'hostid');
+ if (!$upd_db_items) {
+ return [];
+ }
- // getting all inventory links on every affected host
- $itemsOnHostsInfo = API::Item()->get([
- 'output' => ['key_', 'inventory_link', 'hostid'],
- 'filter' => ['hostid' => $hostids],
- 'nopermissions' => true
- ]);
+ $options = [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_keys($upd_db_items)
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
- // now, changing array to: 'hostid' => array('key_'=>'inventory_link')
- $linksOnHostsCurr = [];
- foreach ($itemsOnHostsInfo as $info) {
- // 0 means no link - we are not interested in those ones
- if ($info['inventory_link'] != 0) {
- if (!isset($linksOnHostsCurr[$info['hostid']])) {
- $linksOnHostsCurr[$info['hostid']] = [$info['key_'] => $info['inventory_link']];
- }
- else{
- $linksOnHostsCurr[$info['hostid']][$info['key_']] = $info['inventory_link'];
+ while ($row = DBfetch($result)) {
+ $upd_db_items[$row['itemid']] = $row + $upd_db_items[$row['itemid']];
+ }
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['itemid']]];
+
+ $upd_items[] = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type'],
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ *
+ * @return array
+ */
+ private static function getUpdChildObjectsUsingName(array $items, array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['hostid']][$item['key_']] = $i;
+ }
+ unset($item);
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['parent_hostid']][$upd_db_item['key_']]];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'hostid' => $upd_db_item['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $upd_db_item['host_status']
+ ] + $item;
+
+ $upd_item += [
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+
+ $upd_items[] = $upd_item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getInsChildObjects(array $items, array $upd_db_items, array $tpl_links,
+ array $hostids): array {
+ $ins_items = [];
+
+ $upd_item_keys = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $upd_item_keys[$upd_db_item['hostid']][] = $upd_db_item['key_'];
+ }
+
+ foreach ($items as $item) {
+ $item['uuid'] = '';
+ $item = self::unsetNestedObjectIds($item);
+
+ foreach ($tpl_links[$item['hostid']] as $host) {
+ if (!in_array($host['hostid'], $hostids)
+ || (array_key_exists($host['hostid'], $upd_item_keys)
+ && in_array($item['key_'], $upd_item_keys[$host['hostid']]))) {
+ continue;
}
+
+ $ins_items[] = [
+ 'hostid' => $host['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $host['status']
+ ] + array_diff_key($item, array_flip(['itemid']));
}
}
- $linksOnHostsFuture = [];
+ return $ins_items;
+ }
- foreach ($items as $item) {
- // checking if inventory_link value is a valid number
- if ($update || $item['value_type'] != ITEM_VALUE_TYPE_LOG) {
- // does inventory field with provided number exists?
- if (!isset($possibleHostInventories[$item['inventory_link']])) {
- $maxVar = max(array_keys($possibleHostInventories));
- self::exception(
- ZBX_API_ERROR_PARAMETERS,
- _s('Item "%1$s" cannot populate a missing host inventory field number "%2$d". Choices are: from 0 (do not populate) to %3$d.', $item['name'], $item['inventory_link'], $maxVar)
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ii.itemid,ii.name,ii.type,ii.key_,ii.value_type,ii.templateid,ii.uuid,ii.valuemapid,ii.hostid,'.
+ 'h.status AS host_status'.
+ ' FROM items i,items ii,hosts h'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND ii.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL]).
+ ' AND '.dbConditionInt('i.type', self::SUPPORTED_ITEM_TYPES).
+ $hostids_condition
+ );
+
+ $items = [];
+ $db_items = [];
+ $i = 0;
+ $tpl_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $item = [
+ 'itemid' => $row['itemid'],
+ 'type' => $row['type'],
+ 'templateid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $item += ['uuid' => generateUuidV4()];
+ }
+
+ if ($row['valuemapid'] != 0) {
+ $item += ['valuemapid' => 0];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $tpl_itemids[$i] = $row['itemid'];
+ $item += array_intersect_key($row,
+ array_flip(['key_', 'hostid', 'host_status', 'value_type'])
);
}
}
- if (!isset($linksOnHostsFuture[$item['hostid']])) {
- $linksOnHostsFuture[$item['hostid']] = [$item['key_'] => $item['inventory_link']];
- }
- else {
- $linksOnHostsFuture[$item['hostid']][$item['key_']] = $item['inventory_link'];
- }
+ $items[$i++] = $item;
+ $db_items[$row['itemid']] = $row;
}
- foreach ($linksOnHostsFuture as $hostId => $linkFuture) {
- if (isset($linksOnHostsCurr[$hostId])) {
- $futureSituation = array_merge($linksOnHostsCurr[$hostId], $linksOnHostsFuture[$hostId]);
- }
- else {
- $futureSituation = $linksOnHostsFuture[$hostId];
+ if ($items) {
+ self::updateForce($items, $db_items);
+
+ if ($tpl_itemids) {
+ $items = array_intersect_key($items, $tpl_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($tpl_itemids));
+
+ self::inherit($items, $db_items);
}
- $valuesCount = array_count_values($futureSituation);
+ }
+ }
- // if we have a duplicate inventory links after merging - we are in trouble
- if (max($valuesCount) > 1) {
- // what inventory field caused this conflict?
- $conflictedLink = array_keys($valuesCount, max($valuesCount));
- $conflictedLink = reset($conflictedLink);
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT ii.itemid,ii.name'.
+ ' FROM items i,items ii'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL]).
+ ' AND '.dbConditionInt('i.type', self::SUPPORTED_ITEM_TYPES).
+ $hostids_condition
+ ), 'itemid');
+
+ if ($db_items) {
+ self::deleteForce($db_items);
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param bool $inherited
+ *
+ * @throws APIException
+ */
+ private static function checkInventoryLinks(array $items, array $db_items = [], bool $inherited = false): void {
+ $item_indexes = [];
+ $del_links = [];
- // which of updated items populates this link?
- $beingSavedItemName = '';
- $names = [];
+ $value_types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT];
- foreach ($items as $item) {
- if (count($names) == 2) {
- break;
+ foreach ($items as $i => $item) {
+ $check = false;
+
+ if ($item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL && in_array($item['value_type'], $value_types)) {
+ if (array_key_exists('inventory_link', $item)) {
+ if (!array_key_exists('itemid', $item)) {
+ if ($item['inventory_link'] != 0) {
+ $check = true;
+ $item_indexes[$item['hostid']][] = $i;
+ }
}
-
- if ($item['inventory_link'] == $conflictedLink) {
- if (isset($item['name'])) {
- $beingSavedItemName = $item['name'];
+ else {
+ if ($item['inventory_link'] != 0) {
+ if ($item['inventory_link'] != $db_items[$item['itemid']]['inventory_link']) {
+ $check = true;
+ $item_indexes[$item['hostid']][] = $i;
+
+ if ($db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
+ }
+ }
}
- else {
- $thisItem = API::Item()->get([
- 'output' => ['name'],
- 'filter' => ['itemid' => $item['itemid']],
- 'nopermissions' => true
- ]);
- $beingSavedItemName = $thisItem[0]['name'];
+ elseif ($db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
}
-
- $names[] = $beingSavedItemName;
}
}
+ }
+ elseif (array_key_exists('itemid', $item) && $db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
+ }
- // Case when at least two templates that are being linked have items that populate same inventory field.
- if (count($names) == 2) {
- [$name1, $name2] = $names;
- }
- else {
- // name of the original item that already populates the field
- $originalItem = API::Item()->get([
- 'output' => ['name'],
- 'filter' => [
- 'hostid' => $hostId,
- 'inventory_link' => $conflictedLink
- ],
- 'nopermissions' => true
- ]);
+ if (!$check) {
+ unset($items[$i]);
+ }
+ }
- $name1 = reset($names);
- $name2 = $originalItem[0]['name'];
- }
+ if (!$items) {
+ return;
+ }
- self::exception(
- ZBX_API_ERROR_PARAMETERS,
- _s(
- 'Two items ("%1$s" and "%2$s") cannot populate one host inventory field "%3$s", this would lead to a conflict.',
- $name1,
- $name2,
- $possibleHostInventories[$conflictedLink]['title']
- )
- );
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['hostid', 'inventory_link']], 'fields' => [
+ 'hostid' => ['type' => API_ANY],
+ 'inventory_link' => ['type' => API_ANY]
+ ]];
+
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $items, '/', $error)) {
+ if ($inherited) {
+ $_item_indexes = [];
+
+ foreach ($items as $i => $item) {
+ if (array_key_exists($item['hostid'], $_item_indexes)
+ && array_key_exists($item['inventory_link'], $_item_indexes[$item['hostid']])) {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" and item with key "%3$s" of template "%4$s" to template "%5$s", because they would populate the same inventory field "%6$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" and item with key "%3$s" of template "%4$s" to host "%5$s", because they would populate the same inventory field "%6$s".');
+
+ $_item = $items[$_item_indexes[$item['hostid']][$item['inventory_link']]];
+
+ $templates = DBfetchArrayAssoc(DBselect(
+ 'SELECT i.itemid,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$_item['templateid'], $item['templateid']])
+ ), 'itemid');
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ $inventory_fields = getHostInventories();
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $_item['key_'],
+ $templates[$_item['templateid']]['host'], $item['key_'],
+ $templates[$item['templateid']]['host'], $hosts[0]['host'],
+ $inventory_fields[$item['inventory_link']]['title']
+ ));
+ }
+
+ $_item_indexes[$item['hostid']][$item['inventory_link']] = $i;
+ }
}
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- return true;
+ $options = [
+ 'output' => ['hostid', 'inventory_link', 'key_'],
+ 'filter' => [
+ 'hostid' => array_unique(array_column($items, 'hostid')),
+ 'inventory_link' => array_unique(array_column($items, 'inventory_link'))
+ ]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['hostid'], $del_links)
+ && in_array($row['inventory_link'], $del_links[$row['hostid']])) {
+ continue;
+ }
+
+ foreach ($item_indexes[$row['hostid']] as $i) {
+ if ($row['inventory_link'] == $items[$i]['inventory_link']) {
+ $item = $items[$i];
+
+ if ($inherited) {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because its inventory field "%4$s" is already populated by the item with key "%5$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because its inventory field "%4$s" is already populated by the item with key "%5$s".');
+
+ $template = DBfetch(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['templateid']])
+ ));
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ $inventory_fields = getHostInventories();
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $item['key_'], $template['host'],
+ $hosts[0]['host'], $inventory_fields[$item['inventory_link']]['title'], $row['key_']
+ ));
+ }
+ else {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot assign the inventory field "%1$s" to the item with key "%2$s" of template "%3$s", because it is already populated by the item with key "%4$s"')
+ : _('Cannot assign the inventory field "%1$s" to the item with key "%2$s" of host "%3$s", because it is already populated by the item with key "%4$s".');
+
+ $inventory_fields = getHostInventories();
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error,
+ $inventory_fields[$item['inventory_link']]['title'], $item['key_'], $hosts[0]['host'],
+ $row['key_']
+ ));
+ }
+ }
+ }
+ }
}
- public function addRelatedObjects(array $options, array $result) {
+ protected function addRelatedObjects(array $options, array $result) {
$result = parent::addRelatedObjects($options, $result);
$itemids = array_keys($result);
@@ -1308,4 +1811,170 @@ class CItem extends CItemGeneral {
return $sqlParts;
}
+
+ /**
+ * @param array $db_items
+ */
+ public static function deleteForce(array $db_items): void {
+ self::addInheritedItems($db_items);
+ self::addDependentItems($db_items, $del_ruleids, $db_item_prototypes);
+
+ if ($del_ruleids) {
+ CDiscoveryRuleManager::delete($del_ruleids);
+ }
+
+ if ($db_item_prototypes) {
+ CItemPrototype::deleteForce($db_item_prototypes);
+ }
+
+ $del_itemids = array_keys($db_items);
+
+ self::deleteAffectedGraphs($del_itemids);
+ self::resetGraphsYAxis($del_itemids);
+ self::deleteFromFavoriteGraphs($del_itemids);
+
+ self::deleteAffectedTriggers($del_itemids);
+
+ self::clearHistoryAndTrends($del_itemids);
+
+ DB::delete('graphs_items', ['itemid' => $del_itemids]);
+ DB::delete('widget_field', ['value_itemid' => $del_itemids]);
+ DB::delete('item_discovery', ['itemid' => $del_itemids]);
+ DB::delete('item_parameter', ['itemid' => $del_itemids]);
+ DB::delete('item_preproc', ['itemid' => $del_itemids]);
+ DB::delete('item_rtdata', ['itemid' => $del_itemids]);
+ DB::delete('item_tag', ['itemid' => $del_itemids]);
+ DB::update('items', [
+ 'values' => ['templateid' => 0, 'master_itemid' => 0],
+ 'where' => ['itemid' => $del_itemids]
+ ]);
+ DB::delete('items', ['itemid' => $del_itemids]);
+
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM, $db_items);
+ }
+
+ /**
+ * Add the dependent items of the given items to the given item array. Also add the dependent LLD rules and item
+ * prototypes to the given appropriate variables.
+ *
+ * @param array $db_items
+ * @param array|null $del_ruleids
+ * @param array|null $db_item_prototypes
+ */
+ protected static function addDependentItems(array &$db_items, array &$del_ruleids = null,
+ array &$db_item_prototypes = null): void {
+ $del_ruleids = [];
+ $db_item_prototypes = [];
+
+ $master_itemids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name', 'flags'],
+ 'filter' => ['master_itemid' => $master_itemids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $master_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ if ($row['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
+ $del_ruleids[] = $row['itemid'];
+ }
+ elseif ($row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_item_prototypes[$row['itemid']] = array_diff_key($row, array_flip(['flags']));
+ }
+ else {
+ if (!array_key_exists($row['itemid'], $db_items)) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_items[$row['itemid']] = array_diff_key($row, array_flip(['flags']));
+ }
+ }
+ }
+ } while ($master_itemids);
+ }
+
+ /**
+ * Delete graphs, which would remain without items after the given items deletion.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteAffectedGraphs(array $del_itemids): void {
+ $del_graphids = DBfetchColumn(DBselect(
+ 'SELECT DISTINCT gi.graphid'.
+ ' FROM graphs_items gi'.
+ ' WHERE '.dbConditionId('gi.itemid', $del_itemids).
+ ' AND NOT EXISTS ('.
+ 'SELECT NULL'.
+ ' FROM graphs_items gii'.
+ ' WHERE gii.graphid=gi.graphid'.
+ ' AND '.dbConditionId('gii.itemid', $del_itemids, true).
+ ')'
+ ), 'graphid');
+
+ if ($del_graphids) {
+ CGraphManager::delete($del_graphids);
+ }
+ }
+
+ /**
+ * Delete the latest data graph of the given items from the favorites.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteFromFavoriteGraphs(array $del_itemids): void {
+ DB::delete('profiles', [
+ 'idx' => 'web.favorite.graphids',
+ 'source' => 'itemid',
+ 'value_id' => $del_itemids
+ ]);
+ }
+
+ /**
+ * Clear the history and trends of the given items.
+ *
+ * @param array $del_itemids
+ */
+ private static function clearHistoryAndTrends(array $del_itemids): void {
+ global $DB;
+
+ $table_names = ['events'];
+
+ $timescale_extension = $DB['TYPE'] === ZBX_DB_POSTGRESQL
+ && CHousekeepingHelper::get(CHousekeepingHelper::DB_EXTENSION) === ZBX_DB_EXTENSION_TIMESCALEDB;
+
+ if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) == 1
+ && (!$timescale_extension || CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL) == 0)) {
+ array_push($table_names, 'history', 'history_log', 'history_str', 'history_text', 'history_uint');
+ }
+
+ if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) == 1
+ && (!$timescale_extension || CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL) == 0)) {
+ array_push($table_names, 'trends', 'trends_uint');
+ }
+
+ $ins_housekeeper = [];
+
+ foreach ($del_itemids as $del_itemid) {
+ foreach ($table_names as $table_name) {
+ $ins_housekeeper[] = [
+ 'tablename' => $table_name,
+ 'field' => 'itemid',
+ 'value' => $del_itemid
+ ];
+
+ if (count($ins_housekeeper) == ZBX_DB_MAX_INSERTS) {
+ DB::insertBatch('housekeeper', $ins_housekeeper);
+ $ins_housekeeper = [];
+ }
+ }
+ }
+
+ if ($ins_housekeeper) {
+ DB::insertBatch('housekeeper', $ins_housekeeper);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CItemGeneral.php b/ui/include/classes/api/services/CItemGeneral.php
index c6dcc174301..6c5746e16ce 100644
--- a/ui/include/classes/api/services/CItemGeneral.php
+++ b/ui/include/classes/api/services/CItemGeneral.php
@@ -38,1905 +38,1354 @@ abstract class CItemGeneral extends CApiService {
INTERFACE_TYPE_IPMI
];
- const ERROR_EXISTS_TEMPLATE = 'existsTemplate';
- const ERROR_EXISTS = 'exists';
- const ERROR_NO_INTERFACE = 'noInterface';
- const ERROR_INVALID_KEY = 'invalidKey';
-
- protected $fieldRules;
+ /**
+ * A list of supported preprocessing types.
+ *
+ * @var array
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [];
/**
- * @abstract
+ * A list of preprocessing types that supports the "params" field.
*
- * @param array $options
+ * @var array
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [];
+
+ /**
+ * A list of preprocessing types that supports the error handling.
*
- * @return array
+ * @var array
*/
- abstract public function get($options = []);
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [];
- public function __construct() {
- parent::__construct();
-
- // template - if templated item, value is taken from template item, cannot be changed on host
- // system - values should not be updated
- // host - value should be null for template items
- $this->fieldRules = [
- 'type' => ['template' => 1],
- 'snmp_oid' => ['template' => 1],
- 'hostid' => [],
- 'name' => ['template' => 1],
- 'description' => [],
- 'key_' => ['template' => 1],
- 'master_itemid' => ['template' => 1],
- 'delay' => [],
- 'history' => [],
- 'trends' => [],
- 'status' => [],
- 'discover' => [],
- 'value_type' => ['template' => 1],
- 'trapper_hosts' => [],
- 'units' => ['template' => 1],
- 'formula' => ['template' => 1],
- 'error' => ['system' => 1],
- 'lastlogsize' => ['system' => 1],
- 'logtimefmt' => [],
- 'templateid' => ['system' => 1],
- 'valuemapid' => ['template' => 1],
- 'params' => [],
- 'ipmi_sensor' => ['template' => 1],
- 'authtype' => [],
- 'username' => [],
- 'password' => [],
- 'publickey' => [],
- 'privatekey' => [],
- 'mtime' => ['system' => 1],
- 'flags' => [],
- 'filter' => [],
- 'interfaceid' => ['host' => 1],
- 'inventory_link' => [],
- 'lifetime' => [],
- 'preprocessing' => ['template' => 1],
- 'overrides' => ['template' => 1],
- 'jmx_endpoint' => [],
- 'url' => ['template' => 1],
- 'timeout' => ['template' => 1],
- 'query_fields' => ['template' => 1],
- 'parameters' => ['template' => 1],
- 'posts' => ['template' => 1],
- 'status_codes' => ['template' => 1],
- 'follow_redirects' => ['template' => 1],
- 'post_type' => ['template' => 1],
- 'http_proxy' => ['template' => 1],
- 'headers' => ['template' => 1],
- 'retrieve_mode' => ['template' => 1],
- 'request_method' => ['template' => 1],
- 'output_format' => ['template' => 1],
- 'allow_traps' => [],
- 'ssl_cert_file' => ['template' => 1],
- 'ssl_key_file' => ['template' => 1],
- 'ssl_key_password' => ['template' => 1],
- 'verify_peer' => ['template' => 1],
- 'verify_host' => ['template' => 1]
- ];
+ /**
+ * A list of supported item types.
+ *
+ * @var array
+ */
+ protected const SUPPORTED_ITEM_TYPES = [];
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_NO_INTERFACE => _('Cannot find host interface on "%1$s" for item key "%2$s".')
- ]);
- }
+ /**
+ * A list of field names for each of value types.
+ *
+ * @var array
+ */
+ protected const VALUE_TYPE_FIELD_NAMES = [];
/**
- * Check items data.
+ * Maximum number of inheritable items per iteration.
*
- * Any system field passed to the function will be unset.
+ * @var int
+ */
+ protected const INHERIT_CHUNK_SIZE = 1000;
+
+ /**
+ * @abstract
*
- * @throw APIException
+ * @param array $options
*
- * @param array $items passed by reference
- * @param bool $update
+ * @return array
*/
- protected function checkInput(array &$items, $update = false) {
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_ITEM_TYPES)]
- ]];
- if ($update) {
- unset($api_input_rules['fields']['type']['flags']);
- }
+ abstract public function get($options = []);
- foreach ($items as $num => $item) {
- $data = array_intersect_key($item, $api_input_rules['fields']);
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($num + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
- }
+ /**
+ * @param array $field_names
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected static function validateByType(array $field_names, array &$items, array $db_items = null): void {
+ $checked_fields = array_fill_keys($field_names, ['type' => API_ANY]);
- if ($update) {
- $itemDbFields = ['itemid' => null];
+ foreach ($items as $i => &$item) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => $checked_fields];
+ $db_item = ($db_items === null) ? null : $db_items[$item['itemid']];
+ $item_type = CItemTypeFactory::getObject($item['type']);
- $dbItemsFields = ['itemid', 'templateid'];
- foreach ($this->fieldRules as $field => $rule) {
- if (!isset($rule['system'])) {
- $dbItemsFields[] = $field;
- }
+ if ($db_item === null) {
+ $api_input_rules['fields'] += $item_type::getCreateValidationRules($item);
}
-
- $dbItems = $this->get([
- 'output' => $dbItemsFields,
- 'itemids' => zbx_objectValues($items, 'itemid'),
- 'editable' => true,
- 'preservekeys' => true
- ]);
-
- $dbHosts = API::Host()->get([
- 'output' => ['hostid', 'status', 'name'],
- 'hostids' => zbx_objectValues($dbItems, 'hostid'),
- 'templated_hosts' => true,
- 'editable' => true,
- 'preservekeys' => true
- ]);
- }
- else {
- $itemDbFields = [
- 'name' => null,
- 'key_' => null,
- 'hostid' => null,
- 'type' => null,
- 'value_type' => null,
- 'delay' => null
- ];
-
- $dbHosts = API::Host()->get([
- 'output' => ['hostid', 'status', 'name'],
- 'hostids' => zbx_objectValues($items, 'hostid'),
- 'templated_hosts' => true,
- 'editable' => true,
- 'preservekeys' => true
- ]);
-
- $discovery_rules = [];
-
- if ($this instanceof CItemPrototype) {
- $itemDbFields['ruleid'] = null;
- $druleids = zbx_objectValues($items, 'ruleid');
-
- if ($druleids) {
- $discovery_rules = API::DiscoveryRule()->get([
- 'output' => ['hostid'],
- 'itemids' => $druleids,
- 'preservekeys' => true
- ]);
+ elseif ($db_item['templateid'] != 0) {
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item += array_intersect_key($db_item, array_flip(['allow_traps']));
}
- }
- }
-
- // interfaces
- $interfaces = API::HostInterface()->get([
- 'output' => ['interfaceid', 'hostid', 'type'],
- 'hostids' => zbx_objectValues($dbHosts, 'hostid'),
- 'nopermissions' => true,
- 'preservekeys' => true
- ]);
-
- if ($update) {
- $updateDiscoveredValidator = new CUpdateDiscoveredValidator([
- 'allowed' => ['itemid', 'status'],
- 'messageAllowedField' => _('Cannot update "%2$s" for a discovered item "%1$s".')
- ]);
- foreach ($items as &$item) {
- // check permissions
- if (!array_key_exists($item['itemid'], $dbItems)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ $item += array_intersect_key($db_item, array_flip(['authtype']));
}
- $dbItem = $dbItems[$item['itemid']];
-
- if (array_key_exists('hostid', $item) && bccomp($dbItem['hostid'], $item['hostid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'hostid', _('cannot be changed'))
- );
+ if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
+ && $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
+ $item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
}
- $itemName = array_key_exists('name', $item) ? $item['name'] : $dbItem['name'];
-
- // discovered fields, except status, cannot be updated
- $updateDiscoveredValidator->setObjectName($itemName);
- $this->checkPartialValidator($item, $updateDiscoveredValidator, $dbItem);
-
- $item += [
- 'hostid' => $dbItem['hostid'],
- 'type' => $dbItem['type'],
- 'name' => $dbItem['name'],
- 'key_' => $dbItem['key_'],
- 'flags' => $dbItem['flags']
- ];
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRulesInherited($db_item);
}
- unset($item);
- }
-
- $item_key_parser = new CItemKey();
- $ip_range_parser = new CIPRangeParser([
- 'v6' => ZBX_HAVE_IPV6,
- 'ranges' => false,
- 'usermacros' => true,
- 'macros' => [
- '{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'
- ]
- ]);
- $update_interval_parser = new CUpdateIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => (get_class($this) === 'CItemPrototype')
- ]);
-
- $index = 0;
- foreach ($items as $inum => &$item) {
- $item = $this->clearValues($item);
- $index++;
-
- $fullItem = $items[$inum];
-
- if (!check_db_fields($itemDbFields, $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ elseif ($db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRulesDiscovered();
}
-
- if ($update) {
- $type = array_key_exists('type', $item) ? $item['type'] : $dbItems[$item['itemid']]['type'];
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- $this->validateHTTPCheck($fullItem, $dbItems[$item['itemid']]);
+ else {
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item += array_intersect_key($db_item, array_flip(
+ ['request_method', 'post_type', 'authtype', 'allow_traps']
+ ));
+ }
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ $item += array_intersect_key($db_item, array_flip(['authtype']));
}
- check_db_fields($dbItems[$item['itemid']], $fullItem);
+ $interfaceid_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_IPMI,
+ ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_HTTPAGENT,
+ ITEM_TYPE_SNMP
+ ];
- $this->checkNoParameters(
- $item,
- ['templateid', 'state', 'lastlogsize', 'mtime', 'error'],
- _('Cannot update "%1$s" for item "%2$s".'),
- $item['name']
- );
+ if (in_array($item['type'], $interfaceid_types)) {
+ $opt_interface_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_HTTPAGENT
+ ];
- // apply rules
- foreach ($this->fieldRules as $field => $rules) {
- if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
- $rules['template'] = 1;
+ if (in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ && (!in_array($db_item['type'], $interfaceid_types)
+ || (in_array($item['type'], array_diff($interfaceid_types, $opt_interface_types))
+ && in_array($db_item['type'], $opt_interface_types)
+ && $db_item['interfaceid'] == 0))) {
+ $item += array_intersect_key($db_item, array_flip(['interfaceid']));
}
+ }
+
+ $username_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT
+ ];
- if ((0 != $fullItem['templateid'] && isset($rules['template'])) || isset($rules['system'])) {
- unset($item[$field]);
+ if (in_array($item['type'], [ITEM_TYPE_SSH, ITEM_TYPE_TELNET])) {
+ $opt_username_types = array_diff($username_types, [ITEM_TYPE_SSH, ITEM_TYPE_TELNET]);
- // For templated item and fields that should not be modified, use the value from DB.
- if (array_key_exists($field, $dbItems[$item['itemid']])
- && array_key_exists($field, $fullItem)) {
- $fullItem[$field] = $dbItems[$item['itemid']][$field];
- }
+ if (!in_array($db_item['type'], $username_types)
+ || (in_array($db_item['type'], $opt_username_types) && $db_item['username'] === '')) {
+ $item += array_intersect_key($db_item, array_flip(['username']));
}
}
- if (!isset($item['key_'])) {
- $item['key_'] = $fullItem['key_'];
- }
- if (!isset($item['hostid'])) {
- $item['hostid'] = $fullItem['hostid'];
- }
+ $params_types = [ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_SCRIPT
+ ];
- // If a templated item is being assigned to an interface with a different type, ignore it.
- $itemInterfaceType = itemTypeInterface($dbItems[$item['itemid']]['type']);
+ if (in_array($item['type'], $params_types) && !in_array($db_item['type'], $params_types)) {
+ $item += array_intersect_key($db_item, array_flip(['params']));
+ }
- if ($itemInterfaceType !== INTERFACE_TYPE_ANY && $itemInterfaceType !== INTERFACE_TYPE_OPT
- && $fullItem['templateid']
- && array_key_exists('interfaceid', $item) && array_key_exists($item['interfaceid'], $interfaces)
- && $interfaces[$item['interfaceid']]['type'] != $itemInterfaceType) {
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
- unset($item['interfaceid']);
- }
- }
- else {
- if ($fullItem['type'] == ITEM_TYPE_HTTPAGENT) {
- $this->validateHTTPCheck($fullItem, []);
+ if (in_array($item['type'], $delay_types)) {
+ if (!in_array($db_item['type'], $delay_types)
+ || ($db_item['type'] == ITEM_TYPE_ZABBIX_ACTIVE
+ && strncmp($db_item['key_'], 'mqtt.get', 8) === 0)) {
+ $item += array_intersect_key($db_item, array_flip(['delay']));
+ }
}
- if (!isset($dbHosts[$item['hostid']])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ if ($item['type'] == ITEM_TYPE_DEPENDENT && $db_item['type'] != ITEM_TYPE_DEPENDENT) {
+ $item += array_intersect_key($db_item, array_flip(['master_itemid']));
}
- check_db_fields($itemDbFields, $fullItem);
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $post_types = [ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML];
- $this->checkNoParameters(
- $item,
- ['templateid', 'state'],
- _('Cannot set "%1$s" for item "%2$s".'),
- $item['name']
- );
-
- if ($this instanceof CItemPrototype && (!array_key_exists($fullItem['ruleid'], $discovery_rules)
- || $discovery_rules[$fullItem['ruleid']]['hostid'] != $fullItem['hostid'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('No permissions to referred object or it does not exist!')
- );
+ if (in_array($item['post_type'], $post_types) && !in_array($db_item['post_type'], $post_types)) {
+ $item += array_intersect_key($db_item, array_flip(['posts']));
+ }
}
- }
-
- if ($fullItem['type'] == ITEM_TYPE_CALCULATED) {
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0, 'length' => DB::getFieldLength('items', 'params')],
- 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])]
- ]];
- $data = array_intersect_key($item, $api_input_rules['fields']);
-
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ if ($item['type'] == ITEM_TYPE_IPMI
+ && ($db_item['type'] != ITEM_TYPE_IPMI
+ || ($item['key_'] !== $db_item['key_'] && $db_item['key_'] === 'ipmi.get'))) {
+ $item += array_intersect_key($db_item, array_flip(['ipmi_sensor']));
}
- }
- if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
- if ($update) {
- if ($dbItems[$item['itemid']]['type'] == $fullItem['type']) {
- $flags = API_NOT_EMPTY;
- }
- else {
- $flags = API_REQUIRED | API_NOT_EMPTY;
- }
- }
- else {
- $flags = API_REQUIRED | API_NOT_EMPTY;
+ if ($item['type'] == ITEM_TYPE_JMX && $db_item['type'] != ITEM_TYPE_JMX) {
+ $item += array_intersect_key($db_item, array_flip(['jmx_endpoint']));
}
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'params' => ['type' => API_STRING_UTF8, 'flags' => $flags, 'length' => DB::getFieldLength('items', 'params')],
- 'timeout' => [
- 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
- ? $flags | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
- : $flags | API_ALLOW_USER_MACRO,
- 'in' => '1:'.SEC_PER_MIN
- ],
- 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
- 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
- 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
- ]]
- ]];
-
- $data = array_intersect_key($item, $api_input_rules['fields']);
+ if ($item['type'] == ITEM_TYPE_SNMP && $db_item['type'] != ITEM_TYPE_SNMP) {
+ $item += array_intersect_key($db_item, array_flip(['snmp_oid']));
+ }
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
+ && $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
+ $item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
}
+
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRules($db_item);
}
- $host = $dbHosts[$fullItem['hostid']];
+ $api_input_rules['fields'] += CItemType::getDefaultValidationRules();
- // Validate update interval.
- if (!in_array($fullItem['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])
- && ($fullItem['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($fullItem['key_'], 'mqtt.get', 8) !== 0)
- && !validateDelay($update_interval_parser, 'delay', $fullItem['delay'], $error)) {
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- // For non-numeric types, whichever value was entered in trends field, is overwritten to zero.
- if ($fullItem['value_type'] == ITEM_VALUE_TYPE_STR || $fullItem['value_type'] == ITEM_VALUE_TYPE_LOG
- || $fullItem['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- $item['trends'] = '0';
- }
-
- // Check if the item requires an interface.
- if ($host['status'] == HOST_STATUS_TEMPLATE) {
- unset($item['interfaceid']);
- }
- else {
- $item_interface_type = itemTypeInterface($fullItem['type']);
+ if ($item['type'] == ITEM_TYPE_JMX) {
+ if (array_key_exists('username', $item) || array_key_exists('password', $item)
+ || ($db_item !== null && $db_item['type'] != ITEM_TYPE_JMX)) {
+ $_item = array_intersect_key($item, array_flip(['username', 'password']));
- if ($item_interface_type !== false) {
- if (!array_key_exists('interfaceid', $fullItem) || !$fullItem['interfaceid']) {
- if ($item_interface_type != INTERFACE_TYPE_OPT) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No interface found.'));
- }
+ if ($db_item === null) {
+ $_item += array_fill_keys(['username', 'password'], '');
}
- elseif (!array_key_exists($fullItem['interfaceid'], $interfaces)
- || bccomp($interfaces[$fullItem['interfaceid']]['hostid'], $fullItem['hostid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses host interface from non-parent host.'));
+ else {
+ $_item += array_intersect_key($db_item, array_flip(['username', 'password']));
}
- elseif ($item_interface_type !== INTERFACE_TYPE_ANY && $item_interface_type !== INTERFACE_TYPE_OPT
- && $interfaces[$fullItem['interfaceid']]['type'] != $item_interface_type) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses incorrect interface type.'));
+
+ if (($_item['username'] === '') !== ($_item['password'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('both username and password should be either present or empty')
+ ));
}
}
- // No interface required, just set it to zero.
- else {
- $item['interfaceid'] = 0;
- }
}
- // item key
- if ($fullItem['type'] == ITEM_TYPE_DB_MONITOR) {
- if (!isset($fullItem['flags']) || $fullItem['flags'] != ZBX_FLAG_DISCOVERY_RULE) {
- if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR) == 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Check the key, please. Default example was passed.')
- );
- }
- }
- elseif ($fullItem['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
- if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR_DISCOVERY) == 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Check the key, please. Default example was passed.')
+ if (array_key_exists('query_fields', $item)) {
+ foreach ($item['query_fields'] as $query_field) {
+ if (count($query_field) != 1 || key($query_field) === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/query_fields', _('nonempty key and value pair expected'))
);
}
}
- }
- elseif (($fullItem['type'] == ITEM_TYPE_SSH && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_SSH) == 0)
- || ($fullItem['type'] == ITEM_TYPE_TELNET && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_TELNET) == 0)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Check the key, please. Default example was passed.'));
- }
-
- // key
- if ($item_key_parser->parse($fullItem['key_']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _params($this->getErrorMsg(self::ERROR_INVALID_KEY), [
- $fullItem['key_'], $fullItem['name'], $host['name'], $item_key_parser->getError()
- ])
- );
- }
- if (($fullItem['type'] == ITEM_TYPE_TRAPPER || $fullItem['type'] == ITEM_TYPE_HTTPAGENT)
- && array_key_exists('trapper_hosts', $fullItem) && $fullItem['trapper_hosts'] !== ''
- && !$ip_range_parser->parse($fullItem['trapper_hosts'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', $ip_range_parser->getError())
- );
- }
-
- // jmx
- if ($fullItem['type'] == ITEM_TYPE_JMX) {
- if (!array_key_exists('jmx_endpoint', $fullItem) && !$update) {
- $item['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
- }
- if (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('cannot be empty'))
- );
- }
+ $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- if (($fullItem['username'] === '') !== ($fullItem['password'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'username',
- _('both username and password should be either present or empty'))
- );
- }
- }
- else {
- if (array_key_exists('jmx_endpoint', $item) && $item['jmx_endpoint'] !== '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('should be empty'))
- );
- }
- elseif (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] !== '') {
- $item['jmx_endpoint'] = '';
+ if (strlen($item['query_fields']) > DB::getFieldLength('items', 'query_fields')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/query_fields', _('value is too long')
+ ));
}
}
- // Dependent item.
- if ($fullItem['type'] == ITEM_TYPE_DEPENDENT) {
- if ($update) {
- if (array_key_exists('master_itemid', $item) && !$item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
- ));
- }
- if ($dbItems[$fullItem['itemid']]['type'] != ITEM_TYPE_DEPENDENT
- && !array_key_exists('master_itemid', $item)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
+ if (array_key_exists('headers', $item)) {
+ foreach ($item['headers'] as $name => $value) {
+ if (trim($name) === '' || !is_string($value) || $value === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/headers', _('nonempty key and value pair expected')
));
}
}
- elseif (!array_key_exists('master_itemid', $item) || !$item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
- ));
- }
- if (array_key_exists('master_itemid', $item) && !is_int($item['master_itemid'])
- && !(is_string($item['master_itemid']) && ctype_digit($item['master_itemid']))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.',
- $item['master_itemid'], 'master_itemid'
- ));
- }
- }
- else {
- if (array_key_exists('master_itemid', $item) && $item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('should be empty')
- ));
- }
- $item['master_itemid'] = 0;
- }
- // ssh, telnet
- if ($fullItem['type'] == ITEM_TYPE_SSH || $fullItem['type'] == ITEM_TYPE_TELNET) {
- if ($fullItem['username'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No authentication user name specified.'));
- }
+ $item['headers'] = self::headersArrayToString($item['headers']);
- if ($fullItem['type'] == ITEM_TYPE_SSH && $fullItem['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) {
- if ($fullItem['publickey'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No public key file specified.'));
- }
- if ($fullItem['privatekey'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No private key file specified.'));
- }
+ if (strlen($item['headers']) > DB::getFieldLength('items', 'headers')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/headers', _('value is too long')
+ ));
}
}
+ }
+ unset($item);
+ }
- // Prevent IPMI sensor field being empty if item key is not "ipmi.get".
- if ($fullItem['type'] == ITEM_TYPE_IPMI && $fullItem['key_'] !== 'ipmi.get'
- && (!array_key_exists('ipmi_sensor', $fullItem) || $fullItem['ipmi_sensor'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'ipmi_sensor', _('cannot be empty')
- ));
- }
-
- // snmp trap
- if ($fullItem['type'] == ITEM_TYPE_SNMPTRAP
- && $fullItem['key_'] !== 'snmptrap.fallback' && $item_key_parser->getKey() !== 'snmptrap') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('SNMP trap key is invalid.'));
- }
-
- // snmp oid
- if ($fullItem['type'] == ITEM_TYPE_SNMP
- && (!array_key_exists('snmp_oid', $fullItem) || $fullItem['snmp_oid'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No SNMP OID specified.'));
- }
-
- $this->checkSpecificFields($fullItem, $update ? 'update' : 'create');
+ /**
+ * @param array $items
+ */
+ protected static function validateUniqueness(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['hostid', 'key_']], 'fields' => [
+ 'hostid' => ['type' => API_ANY],
+ 'key_' => ['type' => API_ANY]
+ ]];
- $this->validateItemPreprocessing($fullItem);
- $this->validateTags($item, '/'.$index);
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
+ }
- $this->validateValueMaps($items);
+ /**
+ * @return array
+ */
+ protected static function getTagsValidationRules(): array {
+ return ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
+ 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
+ ]];
+ }
- $this->checkAndAddUuid($items, $dbHosts, $update);
- $this->checkExistingItems($items);
+ /**
+ * @param int $flags
+ *
+ * @return array
+ */
+ public static function getPreprocessingValidationRules(int $flags = 0x00): array {
+ return [
+ 'type' => API_OBJECTS,
+ 'uniq_by_values' => [
+ ['type' => [ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED]],
+ ['type' => [ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ ['type' => [ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ ['type' => [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_PREPROCESSING_TYPES)],
+ 'params' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'type', 'in' => implode(',', static::PREPROC_TYPES_WITH_PARAMS)], 'type' => API_PREPROC_PARAMS, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($flags & API_ALLOW_LLD_MACRO), 'preproc_type' => ['field' => 'type'], 'length' => DB::getFieldLength('item_preproc', 'params')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'params')]
+ ]],
+ 'error_handler' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'type', 'in' => implode(',', array_diff(static::PREPROC_TYPES_WITH_ERR_HANDLING, [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]))], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
+ ['if' => ['field' => 'type', 'in' => ZBX_PREPROC_VALIDATE_NOT_SUPPORTED], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('item_preproc', 'error_handler')]
+ ]],
+ 'error_handler_params' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_VALUE;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
+ ['if' => static function (array $data): bool {
+ return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'error_handler_params')]
+ ]]
+ ]
+ ];
}
/**
- * Check that only items on templates have UUID. Add UUID to all host prototypes on templates,
- * if it doesn't exist.
+ * Check that host IDs of given items are valid.
+ * If host IDs are valid, $db_hosts and $db_templates parameters will be filled with found hosts and templates.
*
- * @param array $items_to_create
- * @param array $db_hosts
- * @param bool $is_update
+ * @param array $items
+ * @param array|null $db_hosts
+ * @param array|null $db_templates
*
* @throws APIException
*/
- protected function checkAndAddUuid(array &$items_to_create, array $db_hosts, bool $is_update): void {
- if ($is_update) {
- foreach ($items_to_create as $index => &$item) {
- if (array_key_exists('uuid', $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1),
- _s('unexpected parameter "%1$s"', 'uuid')
- )
- );
- }
- }
+ protected static function checkHostsAndTemplates(array $items, array &$db_hosts = null,
+ array &$db_templates = null): void {
+ $hostids = array_unique(array_column($items, 'hostid'));
+
+ $db_templates = API::Template()->get([
+ 'output' => [],
+ 'templateids' => $hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
- return;
+ $_hostids = array_diff($hostids, array_keys($db_templates));
+
+ $db_hosts = $_hostids
+ ? API::Host()->get([
+ 'output' => ['status'],
+ 'hostids' => $_hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ])
+ : [];
+
+ if (count($db_templates) + count($db_hosts) != count($hostids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
}
+ }
- foreach ($items_to_create as $index => &$item) {
- if ($db_hosts[$item['hostid']]['status'] != HOST_STATUS_TEMPLATE && array_key_exists('uuid', $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1), _s('unexpected parameter "%1$s"', 'uuid'))
- );
+ /**
+ * Add host_status property to given items in accordance of given hosts and templates statuses.
+ *
+ * @param array $items
+ * @param array $db_hosts
+ * @param array $db_templates
+ */
+ protected static function addHostStatus(array &$items, array $db_hosts, array $db_templates): void {
+ foreach ($items as &$item) {
+ if (array_key_exists($item['hostid'], $db_templates)) {
+ $item['host_status'] = HOST_STATUS_TEMPLATE;
}
-
- if ($db_hosts[$item['hostid']]['status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
- $item['uuid'] = generateUuidV4();
+ else {
+ $item['host_status'] = $db_hosts[$item['hostid']]['status'];
}
}
unset($item);
-
- $db_uuid = DB::select('items', [
- 'output' => ['uuid'],
- 'filter' => ['uuid' => array_column($items_to_create, 'uuid')],
- 'limit' => 1
- ]);
-
- if ($db_uuid) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Entry with UUID "%1$s" already exists.', $db_uuid[0]['uuid'])
- );
- }
}
/**
- * Validates tags.
+ * Add flags property to given items with the given flags value.
*
- * @param array $item
- * @param array $item['tags']
- * @param string $item['tags'][]['tag']
- * @param string $item['tags'][]['value']
- *
- * @throws APIException if the input is invalid.
+ * @param array $items
+ * @param int $flags
*/
- protected function validateTags(array $item, string $path = '/') {
- if (!array_key_exists('tags', $item)) {
- return;
- }
-
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'tags' => ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
- 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
- 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
- ]]
- ]];
-
- $item_tags = ['tags' => $item['tags']];
- if (!CApiInputValidator::validate($api_input_rules, $item_tags, $path, $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ protected static function addFlags(array &$items, int $flags): void {
+ foreach ($items as &$item) {
+ $item['flags'] = $flags;
}
+ unset($item);
}
/**
- * Check item specific fields. Each API like Item, Itemprototype and Discovery rule may inherit different fields
- * to validate.
+ * Check and add UUID to all item prototypes on templates, if it doesn't exist.
*
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
*
- * @return bool
+ * @throws APIException
*/
- abstract protected function checkSpecificFields(array $item, $method);
-
- protected function clearValues(array $item) {
- if (isset($item['port']) && $item['port'] != '') {
- $item['port'] = ltrim($item['port'], '0');
- if ($item['port'] == '') {
- $item['port'] = 0;
+ protected static function checkAndAddUuid(array &$items): void {
+ foreach ($items as &$item) {
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
+ $item['uuid'] = generateUuidV4();
}
}
+ unset($item);
+
+ $uuids = array_column($items, 'uuid');
- if (array_key_exists('type', $item) &&
- ($item['type'] == ITEM_TYPE_DEPENDENT || $item['type'] == ITEM_TYPE_TRAPPER
- || ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && array_key_exists('key_', $item)
- && strncmp($item['key_'], 'mqtt.get', 8) === 0))) {
- $item['delay'] = 0;
+ if (!$uuids) {
+ return;
}
- return $item;
- }
+ $duplicates = DB::select('items', [
+ 'output' => ['uuid'],
+ 'filter' => ['uuid' => $uuids],
+ 'limit' => 1
+ ]);
- protected function errorInheritFlags($flag, $key, $host) {
- switch ($flag) {
- case ZBX_FLAG_DISCOVERY_NORMAL:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_RULE:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as a discovery rule.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_PROTOTYPE:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item prototype.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_CREATED:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item created from item prototype.', $key, $host));
- break;
- default:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as unknown item element.', $key, $host));
+ if ($duplicates) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Entry with UUID "%1$s" already exists.', $duplicates[0]['uuid'])
+ );
}
}
/**
- * Return first main interface matched from list of preferred types, or NULL.
- *
- * @param array $interfaces An array of interfaces to choose from.
+ * @param array $items
+ * @param array|null $hostids
*
- * @return ?array
+ * @return array
*/
- public static function findInterfaceByPriority(array $interfaces): ?array {
- $interface_by_type = [];
+ protected static function getTemplateLinks(array $items, ?array $hostids): array {
+ if ($hostids !== null) {
+ $db_hosts = DB::select('hosts', [
+ 'output' => ['hostid', 'status'],
+ 'hostids' => $hostids,
+ 'preservekeys' => true
+ ]);
- foreach ($interfaces as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interface_by_type[$interface['type']] = $interface;
+ $tpl_links = [];
+
+ foreach ($items as $item) {
+ $tpl_links[$item['hostid']] = $db_hosts;
}
}
+ else {
+ $templateids = [];
- foreach (self::INTERFACE_TYPES_BY_PRIORITY as $interface_type) {
- if (array_key_exists($interface_type, $interface_by_type)) {
- return $interface_by_type[$interface_type];
+ foreach ($items as $item) {
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE) {
+ $templateids[$item['hostid']] = true;
+ }
}
- }
- return null;
- }
+ if (!$templateids) {
+ return [];
+ }
- /**
- * Returns the interface that best matches the given item.
- *
- * @param array $item_type An item type
- * @param array $interfaces An array of interfaces to choose from
- *
- * @return array|boolean The best matching interface;
- * an empty array of no matching interface was found;
- * false, if the item does not need an interface
- */
- public static function findInterfaceForItem($item_type, array $interfaces) {
- $type = itemTypeInterface($item_type);
+ $result = DBselect(
+ 'SELECT ht.templateid,ht.hostid,h.status'.
+ ' FROM hosts_templates ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_keys($templateids))
+ );
- if ($type == INTERFACE_TYPE_OPT) {
- return false;
- }
- elseif ($type == INTERFACE_TYPE_ANY) {
- return self::findInterfaceByPriority($interfaces);
- }
- // the item uses a specific type of interface
- elseif ($type !== false) {
- $interface_by_type = [];
+ $tpl_links = [];
- foreach ($interfaces as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interface_by_type[$interface['type']] = $interface;
- }
+ while ($row = DBfetch($result)) {
+ $tpl_links[$row['templateid']][$row['hostid']] = [
+ 'hostid' => $row['hostid'],
+ 'status' => $row['status']
+ ];
}
-
- return array_key_exists($type, $interface_by_type) ? $interface_by_type[$type] : [];
- }
- // the item does not need an interface
- else {
- return false;
}
+
+ return $tpl_links;
}
/**
- * Updates the children of the item on the given hosts and propagates the inheritance to the child hosts.
+ * Filter out inheritable items from the given items.
*
- * @param array $tpl_items An array of items to inherit.
- * @param array|null $hostids An array of hosts to inherit to; if set to null, the items will be inherited to all
- * linked hosts or templates.
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
*/
- protected function inherit(array $tpl_items, array $hostids = null) {
- $tpl_items = zbx_toHash($tpl_items, 'itemid');
-
- // Inherit starting from common items and finishing up dependent.
- while ($tpl_items) {
- $_tpl_items = [];
+ protected static function filterObjectsToInherit(array &$items, array &$db_items, array $tpl_links): void {
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['hostid'], $tpl_links)) {
+ unset($items[$i]);
- foreach ($tpl_items as $tpl_item) {
- if ($tpl_item['type'] != ITEM_TYPE_DEPENDENT
- || !array_key_exists($tpl_item['master_itemid'], $tpl_items)) {
- $_tpl_items[$tpl_item['itemid']] = $tpl_item;
+ if (array_key_exists($item['itemid'], $db_items)) {
+ unset($db_items[$item['itemid']]);
}
}
-
- foreach ($_tpl_items as $itemid => $_tpl_item) {
- unset($tpl_items[$itemid]);
- }
-
- $this->_inherit($_tpl_items, $hostids);
}
}
/**
- * Auxiliary method for item inheritance. See full description in inherit() method.
+ * Check that no items with repeating keys would be inherited to a single host or template.
+ *
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ *
+ * @throws APIException
*/
- private function _inherit(array $tpl_items, array $hostids = null) {
- // Prepare the child items.
- $new_items = $this->prepareInheritedItems($tpl_items, $hostids);
- if (!$new_items) {
- return;
- }
+ protected static function checkDoubleInheritedNames(array $items, array $db_items, array $tpl_links): void {
+ $item_indexes = [];
- $ins_items = [];
- $upd_items = [];
-
- foreach ($new_items as $new_item) {
- if (array_key_exists('itemid', $new_item)) {
- if ($this instanceof CItemPrototype) {
- unset($new_item['ruleid']);
- }
- $upd_items[$new_item['itemid']] = $new_item;
- }
- else {
- $ins_items[] = $new_item;
+ foreach ($items as $i => $item) {
+ if (array_key_exists($item['itemid'], $db_items) && $item['key_'] === $db_items[$item['itemid']]['key_']) {
+ continue;
}
- }
- $this->validateDependentItems($new_items);
+ $item_indexes[$item['key_']][] = $i;
+ }
- // Save the new items.
- if ($ins_items) {
- if ($this instanceof CItem) {
- static::validateInventoryLinks($ins_items, false);
+ foreach ($item_indexes as $key => $indexes) {
+ if (count($indexes) == 1) {
+ continue;
}
- $this->createReal($ins_items);
- }
+ $tpl_items = array_column(array_intersect_key($items, array_flip($indexes)), null, 'hostid');
+ $templateids = array_keys($tpl_items);
+ $template_count = count($templateids) - 1;
- if ($upd_items) {
- if ($this instanceof CItem) {
- static::validateInventoryLinks($upd_items, true);
- }
+ for ($i = 0; $i < $template_count - 1; $i++) {
+ for ($j = $i + 1; $j < $template_count; $j++) {
+ $same_hosts = array_intersect_key($tpl_links[$templateids[$i]], $tpl_links[$templateids[$j]]);
- $this->updateReal($upd_items);
- }
+ if ($same_hosts) {
+ $same_host = reset($same_hosts);
- $new_items = array_merge($upd_items, $ins_items);
+ $hosts = DB::select('hosts', [
+ 'output' => ['hostid', 'host'],
+ 'hostids' => [$templateids[$i], $templateids[$j], $same_host['hostid']],
+ 'preservekeys' => true
+ ]);
- // Inheriting items from the templates.
- $db_items = DBselect(
- 'SELECT i.itemid'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid'.
- ' AND '.dbConditionInt('i.itemid', zbx_objectValues($new_items, 'itemid')).
- ' AND '.dbConditionInt('h.status', [HOST_STATUS_TEMPLATE])
- );
+ $target_is_host = in_array($same_host['status'],
+ [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]
+ );
- $tpl_itemids = [];
- while ($db_item = DBfetch($db_items)) {
- $tpl_itemids[$db_item['itemid']] = true;
- }
+ switch ($tpl_items[$templateids[$i]]['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+ }
- foreach ($new_items as $index => $new_item) {
- if (!array_key_exists($new_item['itemid'], $tpl_itemids)) {
- unset($new_items[$index]);
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key,
+ $hosts[$templateids[$i]]['host'], $hosts[$templateids[$j]]['host'],
+ $hosts[$same_host['hostid']]['host']
+ ));
+ }
+ }
}
}
-
- $this->inherit($new_items);
}
/**
- * Prepares and returns an array of child items, inherited from items $tpl_items on the given hosts.
- *
- * @param array $tpl_items
- * @param string $tpl_items[<itemid>]['itemid']
- * @param string $tpl_items[<itemid>]['hostid']
- * @param string $tpl_items[<itemid>]['key_']
- * @param int $tpl_items[<itemid>]['type']
- * @param array $tpl_items[<itemid>]['preprocessing'] (optional)
- * @param int $tpl_items[<itemid>]['preprocessing'][]['type']
- * @param string $tpl_items[<itemid>]['preprocessing'][]['params']
- * @param int $tpl_items[<itemid>]['flags']
- * @param string $tpl_items[<itemid>]['master_itemid'] (optional)
- * @param mixed $tpl_items[<itemid>][<field_name>] (optional)
- * @param array|null $hostids
+ * Get item chunks to inherit.
*
- * @return array an array of unsaved child items
+ * @param array $items
+ * @param array $tpl_links
+ *
+ * @return array
*/
- private function prepareInheritedItems(array $tpl_items, array $hostids = null) {
- $itemids_by_templateid = [];
- foreach ($tpl_items as $tpl_item) {
- $itemids_by_templateid[$tpl_item['hostid']][] = $tpl_item['itemid'];
- }
-
- // Fetch all child hosts.
- $chd_hosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectParentTemplates' => ['templateid'],
- 'selectInterfaces' => ['interfaceid', 'main', 'type'],
- 'templateids' => array_keys($itemids_by_templateid),
- 'hostids' => $hostids,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
- if (!$chd_hosts) {
- return [];
- }
+ protected static function getInheritChunks(array $items, array $tpl_links): array {
+ $chunks = [
+ [
+ 'item_indexes' => [],
+ 'hosts' => [],
+ 'size' => 0
+ ]
+ ];
+ $last = 0;
- $chd_items_tpl = [];
- $chd_items_key = [];
+ foreach ($items as $i => $item) {
+ $hosts_chunks = array_chunk($tpl_links[$item['hostid']], self::INHERIT_CHUNK_SIZE, true);
- // Preparing list of items by item templateid.
- $sql = 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.templateid', zbx_objectValues($tpl_items, 'itemid'));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
- }
- $db_items = DBselect($sql);
+ foreach ($hosts_chunks as $hosts) {
+ if ($chunks[$last]['size'] < self::INHERIT_CHUNK_SIZE) {
+ $_hosts = array_slice($hosts, 0, self::INHERIT_CHUNK_SIZE - $chunks[$last]['size'], true);
- while ($db_item = DBfetch($db_items)) {
- $hostid = $db_item['hostid'];
- unset($db_item['hostid']);
+ $can_add_hosts = true;
- $chd_items_tpl[$hostid][$db_item['templateid']] = $db_item;
- }
+ foreach ($chunks[$last]['item_indexes'] as $_i) {
+ $new_hosts = array_diff_key($_hosts, $chunks[$last]['hosts']);
- $hostids_by_key = [];
+ if (array_intersect_key($tpl_links[$items[$_i]['hostid']], $new_hosts)) {
+ $can_add_hosts = false;
+ break;
+ }
+ }
- // Preparing list of items by item key.
- foreach ($chd_hosts as $chd_host) {
- $tpl_itemids = [];
+ if ($can_add_hosts) {
+ $chunks[$last]['item_indexes'][] = $i;
+ $chunks[$last]['hosts'] += $_hosts;
+ $chunks[$last]['size'] += count($_hosts);
- foreach ($chd_host['parentTemplates'] as $parent_template) {
- if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
- $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ $hosts = array_diff_key($hosts, $_hosts);
+ }
}
- }
- foreach ($tpl_itemids as $tpl_itemid) {
- if (!array_key_exists($chd_host['hostid'], $chd_items_tpl)
- || !array_key_exists($tpl_itemid, $chd_items_tpl[$chd_host['hostid']])) {
- $hostids_by_key[$tpl_items[$tpl_itemid]['key_']][] = $chd_host['hostid'];
+ if ($hosts) {
+ $chunks[++$last] = [
+ 'item_indexes' => [$i],
+ 'hosts' => $hosts,
+ 'size' => count($hosts)
+ ];
}
}
}
- foreach ($hostids_by_key as $key_ => $key_hostids) {
- $sql_select = ($this instanceof CItemPrototype) ? ',id.parent_itemid AS ruleid' : '';
- // "LEFT JOIN" is needed to check flags on inherited and existing item, item prototype or lld rule.
- // For example, when linking an item prototype with same key as in an item on target host or template.
- $sql_join = ($this instanceof CItemPrototype) ? ' LEFT JOIN item_discovery id ON i.itemid=id.itemid' : '';
- $db_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.$sql_select.
- ' FROM items i'.$sql_join.
- ' WHERE '.dbConditionInt('i.hostid', $key_hostids).
- ' AND '.dbConditionString('i.key_', [$key_])
- );
-
- while ($db_item = DBfetch($db_items)) {
- $hostid = $db_item['hostid'];
- unset($db_item['hostid']);
+ return $chunks;
+ }
- $chd_items_key[$hostid][$db_item['key_']] = $db_item;
- }
- }
+ /**
+ * @param array $item
+ * @param array $upd_db_item
+ *
+ * @throws APIException
+ */
+ protected static function showObjectMismatchError(array $item, array $upd_db_item): void {
+ $target_is_host = in_array($upd_db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
- // List of the discovery rules.
- if ($this instanceof CItemPrototype) {
- // List of itemids without 'ruleid' property.
- $tpl_itemids = [];
- $tpl_ruleids = [];
- foreach ($tpl_items as $tpl_item) {
- if (!array_key_exists('ruleid', $tpl_item)) {
- $tpl_itemids[] = $tpl_item['itemid'];
- }
- else {
- $tpl_ruleids[$tpl_item['ruleid']] = true;
- }
- }
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => [$item['hostid'], $upd_db_item['hostid']],
+ 'preservekeys' => true
+ ]);
- if ($tpl_itemids) {
- $db_rules = DBselect(
- 'SELECT id.parent_itemid,id.itemid'.
- ' FROM item_discovery id'.
- ' WHERE '.dbConditionInt('id.itemid', $tpl_itemids)
- );
+ $error = '';
- while ($db_rule = DBfetch($db_rules)) {
- $tpl_items[$db_rule['itemid']]['ruleid'] = $db_rule['parent_itemid'];
- $tpl_ruleids[$db_rule['parent_itemid']] = true;
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
}
- }
-
- $sql = 'SELECT i.hostid,i.templateid,i.itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.templateid', array_keys($tpl_ruleids));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
- }
- $db_rules = DBselect($sql);
- // List of child lld ruleids by child hostid and parent lld ruleid.
- $chd_ruleids = [];
- while ($db_rule = DBfetch($db_rules)) {
- $chd_ruleids[$db_rule['hostid']][$db_rule['templateid']] = $db_rule['itemid'];
- }
- }
+ case ZBX_FLAG_DISCOVERY_RULE:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
+ }
- $new_items = [];
- // List of the updated item keys by hostid.
- $upd_hostids_by_key = [];
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
- foreach ($chd_hosts as $chd_host) {
- $tpl_itemids = [];
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
+ break 2;
- foreach ($chd_host['parentTemplates'] as $parent_template) {
- if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
- $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
}
- }
-
- foreach ($tpl_itemids as $tpl_itemid) {
- $tpl_item = $tpl_items[$tpl_itemid];
-
- $chd_item = null;
+ }
- // Update by templateid.
- if (array_key_exists($chd_host['hostid'], $chd_items_tpl)
- && array_key_exists($tpl_item['itemid'], $chd_items_tpl[$chd_host['hostid']])) {
- $chd_item = $chd_items_tpl[$chd_host['hostid']][$tpl_item['itemid']];
+ if ($error) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'],
+ $hosts[$item['hostid']]['host'], $hosts[$upd_db_item['hostid']]['host']
+ ));
+ }
- if ($tpl_item['key_'] !== $chd_item['key_']) {
- $upd_hostids_by_key[$tpl_item['key_']][] = $chd_host['hostid'];
- }
- }
- // Update by key.
- elseif (array_key_exists($chd_host['hostid'], $chd_items_key)
- && array_key_exists($tpl_item['key_'], $chd_items_key[$chd_host['hostid']])) {
- $chd_item = $chd_items_key[$chd_host['hostid']][$tpl_item['key_']];
+ if ($upd_db_item['templateid'] == 0) {
+ return;
+ }
- // Check if an item of a different type with the same key exists.
- if ($tpl_item['flags'] != $chd_item['flags']) {
- $this->errorInheritFlags($chd_item['flags'], $chd_item['key_'], $chd_host['host']);
- }
+ $template = DBfetch(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$upd_db_item['templateid']])
+ ));
- // Check if item already linked to another template.
- if ($chd_item['templateid'] != 0 && bccomp($chd_item['templateid'], $tpl_item['itemid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params(
- $this->getErrorMsg(self::ERROR_EXISTS_TEMPLATE), [$tpl_item['key_'], $chd_host['host']]
- ));
- }
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
- if ($this instanceof CItemPrototype) {
- $chd_ruleid = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
- if (bccomp($chd_item['ruleid'], $chd_ruleid) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item prototype "%1$s" already exists on "%2$s", linked to another rule.',
- $chd_item['key_'], $chd_host['host']
- )
- );
- }
- }
- }
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
- // copying item
- $new_item = $tpl_item;
- $new_item['uuid'] = '';
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
+ }
- if ($chd_item !== null) {
- $new_item['itemid'] = $chd_item['itemid'];
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'], $hosts[$item['hostid']]['host'],
+ $hosts[$upd_db_item['hostid']]['host'], $template['host']
+ ));
+ }
- if ($new_item['type'] == ITEM_TYPE_HTTPAGENT) {
- $new_item['interfaceid'] = null;
- }
- }
- else {
- unset($new_item['itemid']);
- if ($this instanceof CItemPrototype) {
- $new_item['ruleid'] = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
- }
- }
- $new_item['hostid'] = $chd_host['hostid'];
- $new_item['templateid'] = $tpl_item['itemid'];
+ /**
+ * @param array $item
+ *
+ * @return array
+ */
+ protected static function unsetNestedObjectIds(array $item): array {
+ if (array_key_exists('tags', $item)) {
+ foreach ($item['tags'] as &$tag) {
+ unset($tag['itemtagid']);
+ }
+ unset($tag);
+ }
- if ($chd_host['status'] != HOST_STATUS_TEMPLATE) {
- if ($chd_item === null || $new_item['type'] != $chd_item['type']) {
- $interface = self::findInterfaceForItem($new_item['type'], $chd_host['interfaces']);
+ if (array_key_exists('preprocessing', $item)) {
+ foreach ($item['preprocessing'] as &$preprocessing) {
+ unset($preprocessing['item_preprocid']);
+ }
+ unset($preprocessing);
+ }
- if ($interface) {
- $new_item['interfaceid'] = $interface['interfaceid'];
- }
- elseif ($interface !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params(
- $this->getErrorMsg(self::ERROR_NO_INTERFACE), [$chd_host['host'], $new_item['key_']]
- ));
- }
- }
+ if (array_key_exists('parameters', $item)) {
+ foreach ($item['parameters'] as &$parameter) {
+ unset($parameter['item_parameterid']);
+ }
+ unset($parameter);
+ }
- if ($this instanceof CItem || $this instanceof CDiscoveryRule) {
- if (!array_key_exists('itemid', $new_item)) {
- $new_item['rtdata'] = true;
- }
- }
- }
+ return $item;
+ }
- if (array_key_exists('preprocessing', $new_item)) {
- foreach ($new_item['preprocessing'] as $preprocessing) {
- if ($chd_item) {
- $preprocessing['itemid'] = $chd_item['itemid'];
- }
- else {
- unset($preprocessing['itemid']);
- }
- }
- }
+ /**
+ * Update relation to master item for inherited dependent items.
+ *
+ * @param array $upd_items
+ * @param array $ins_items
+ * @param array $hostids
+ */
+ protected static function setChildMasterItemIds(array &$upd_items, array &$ins_items, array $hostids): void {
+ $upd_item_indexes = [];
+ $ins_item_indexes = [];
- $new_items[] = $new_item;
+ foreach ($upd_items as $i => $upd_item) {
+ if ($upd_item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('master_itemid', $upd_item)) {
+ $upd_item_indexes[$upd_item['master_itemid']][$upd_item['hostid']][] = $i;
}
}
- // Check if item with a new key already exists on the child host.
- if ($upd_hostids_by_key) {
- $sql_where = [];
- foreach ($upd_hostids_by_key as $key => $hostids) {
- $sql_where[] = dbConditionInt('i.hostid', $hostids).' AND i.key_='.zbx_dbstr($key);
+ foreach ($ins_items as $i => $ins_item) {
+ if ($ins_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $ins_item_indexes[$ins_item['master_itemid']][$ins_item['hostid']][] = $i;
}
+ }
- $sql = 'SELECT i.hostid,i.key_'.
- ' FROM items i'.
- ' WHERE ('.implode(') OR (', $sql_where).')';
- $db_items = DBselect($sql, 1);
+ if (!$upd_item_indexes && !$ins_item_indexes) {
+ return;
+ }
- if ($db_item = DBfetch($db_items)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params($this->getErrorMsg(self::ERROR_EXISTS),
- [$db_item['key_'], $chd_hosts[$db_item['hostid']]['host']]
- ));
+ $options = [
+ 'output' => ['itemid', 'hostid', 'templateid'],
+ 'filter' => [
+ 'templateid' => array_keys($ins_item_indexes + $upd_item_indexes),
+ 'hostid' => $hostids
+ ]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['templateid'], $upd_item_indexes)
+ && array_key_exists($row['hostid'], $upd_item_indexes[$row['templateid']])) {
+ foreach ($upd_item_indexes[$row['templateid']][$row['hostid']] as $i) {
+ $upd_items[$i]['master_itemid'] = $row['itemid'];
+ }
}
- }
- return $this->prepareDependentItems($tpl_items, $new_items, $hostids);
+ if (array_key_exists($row['templateid'], $ins_item_indexes)
+ && array_key_exists($row['hostid'], $ins_item_indexes[$row['templateid']])) {
+ foreach ($ins_item_indexes[$row['templateid']][$row['hostid']] as $i) {
+ $ins_items[$i]['master_itemid'] = $row['itemid'];
+ }
+ }
+ }
}
/**
- * Update relations for inherited dependent items to master items.
- *
- * @param array $tpl_items
- * @param int $tpl_items[<itemid>]['type']
- * @param string $tpl_items[<itemid>]['master_itemid']
- * @param array $new_items
- * @param string $new_items[<itemid>]['hostid']
- * @param int $new_items[<itemid>]['type']
- * @param string $new_items[<itemid>]['templateid']
- * @param array|null $hostids
+ * @param array $upd_items
+ * @param array $upd_db_items
+ * @param array $ins_items
*
- * @return array an array of synchronized inherited items.
+ * @throws APIException
*/
- private function prepareDependentItems(array $tpl_items, array $new_items, array $hostids = null) {
- $tpl_master_itemids = [];
+ protected static function addInterfaceIds(array &$upd_items, array $upd_db_items, array &$ins_items): void {
+ $upd_item_indexes = [];
+ $ins_item_indexes = [];
+ $interface_types = [];
- foreach ($tpl_items as $tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_DEPENDENT) {
- $tpl_master_itemids[$tpl_item['master_itemid']] = true;
- }
- }
+ $required_interface_types = [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_IPMI,
+ INTERFACE_TYPE_JMX
+ ];
- if ($tpl_master_itemids) {
- $sql = 'SELECT i.itemid,i.hostid,i.templateid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.templateid', array_keys($tpl_master_itemids));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionId('i.hostid', $hostids);
+ $upd_item_indexes_by_interfaceid = [];
+
+ foreach ($upd_items as $i => $upd_item) {
+ if (!in_array($upd_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ continue;
}
- $db_items = DBselect($sql);
- $master_links = [];
+ $interface_type = itemTypeInterface($upd_item['type']);
- while ($db_item = DBfetch($db_items)) {
- $master_links[$db_item['templateid']][$db_item['hostid']] = $db_item['itemid'];
+ if (!in_array($interface_type, $required_interface_types)) {
+ continue;
}
- foreach ($new_items as &$new_item) {
- if ($new_item['type'] == ITEM_TYPE_DEPENDENT) {
- $tpl_item = $tpl_items[$new_item['templateid']];
+ if ($upd_db_items[$upd_item['itemid']]['interfaceid'] != 0) {
+ $upd_item_indexes_by_interfaceid[$upd_db_items[$upd_item['itemid']]['interfaceid']][] = $i;
+ }
+ else {
+ $upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
+ }
+ }
+
+ if ($upd_item_indexes_by_interfaceid) {
+ $options = [
+ 'output' => ['interfaceid', 'type'],
+ 'interfaceids' => array_keys($upd_item_indexes_by_interfaceid)
+ ];
+ $result = DBselect(DB::makeSql('interface', $options));
+
+ while ($row = DBfetch($result)) {
+ foreach ($upd_item_indexes_by_interfaceid[$row['interfaceid']] as $i) {
+ $upd_item = $upd_items[$i];
+ $interface_type = itemTypeInterface($upd_item['type']);
- if (array_key_exists('master_itemid', $tpl_item)) {
- $new_item['master_itemid'] = $master_links[$tpl_item['master_itemid']][$new_item['hostid']];
+ if ($interface_type != $row['type']) {
+ $upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
}
}
}
- unset($new_item);
}
- return $new_items;
- }
+ foreach ($ins_items as $i => $ins_item) {
+ if (!in_array($ins_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ continue;
+ }
- /**
- * Validate item pre-processing.
- *
- * @param array $item An array of single item data.
- * @param array $item['preprocessing'] An array of item pre-processing data.
- * @param string $item['preprocessing'][]['type'] The preprocessing option type. Possible values:
- * 1 - ZBX_PREPROC_MULTIPLIER;
- * 2 - ZBX_PREPROC_RTRIM;
- * 3 - ZBX_PREPROC_LTRIM;
- * 4 - ZBX_PREPROC_TRIM;
- * 5 - ZBX_PREPROC_REGSUB;
- * 6 - ZBX_PREPROC_BOOL2DEC;
- * 7 - ZBX_PREPROC_OCT2DEC;
- * 8 - ZBX_PREPROC_HEX2DEC;
- * 9 - ZBX_PREPROC_DELTA_VALUE;
- * 10 - ZBX_PREPROC_DELTA_SPEED;
- * 11 - ZBX_PREPROC_XPATH;
- * 12 - ZBX_PREPROC_JSONPATH;
- * 13 - ZBX_PREPROC_VALIDATE_RANGE;
- * 14 - ZBX_PREPROC_VALIDATE_REGEX;
- * 15 - ZBX_PREPROC_VALIDATE_NOT_REGEX;
- * 16 - ZBX_PREPROC_ERROR_FIELD_JSON;
- * 17 - ZBX_PREPROC_ERROR_FIELD_XML;
- * 18 - ZBX_PREPROC_ERROR_FIELD_REGEX;
- * 19 - ZBX_PREPROC_THROTTLE_VALUE;
- * 20 - ZBX_PREPROC_THROTTLE_TIMED_VALUE;
- * 21 - ZBX_PREPROC_SCRIPT;
- * 22 - ZBX_PREPROC_PROMETHEUS_PATTERN;
- * 23 - ZBX_PREPROC_PROMETHEUS_TO_JSON;
- * 24 - ZBX_PREPROC_CSV_TO_JSON;
- * 25 - ZBX_PREPROC_STR_REPLACE;
- * 26 - ZBX_PREPROC_VALIDATE_NOT_SUPPORTED;
- * @param string $item['preprocessing'][]['params'] Additional parameters used by preprocessing
- * option. Multiple parameters are separated by LF
- * (\n) character.
- * @param string $item['preprocessing'][]['error_handler'] Action type used in case of preprocessing step
- * failure. Possible values:
- * 0 - ZBX_PREPROC_FAIL_DEFAULT;
- * 1 - ZBX_PREPROC_FAIL_DISCARD_VALUE;
- * 2 - ZBX_PREPROC_FAIL_SET_VALUE;
- * 3 - ZBX_PREPROC_FAIL_SET_ERROR.
- * @param string $item['preprocessing'][]['error_handler_params'] Error handler parameters.
- */
- protected function validateItemPreprocessing(array $item) {
- if (array_key_exists('preprocessing', $item)) {
- if (!is_array($item['preprocessing'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ $interface_type = itemTypeInterface($ins_item['type']);
+
+ if (!in_array($interface_type, $required_interface_types)) {
+ continue;
}
- $type_validator = new CLimitedSetValidator(['values' => static::SUPPORTED_PREPROCESSING_TYPES]);
+ $ins_item_indexes[$ins_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
+ }
- $error_handler_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE,
- ZBX_PREPROC_FAIL_SET_ERROR
- ]
- ]);
+ if (!$upd_item_indexes && !$ins_item_indexes) {
+ return;
+ }
- $unsupported_error_handler_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR]
- ]);
+ $options = [
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'filter' => [
+ 'hostid' => array_keys($upd_item_indexes + $ins_item_indexes),
+ 'type' => array_keys($interface_types),
+ 'main' => INTERFACE_PRIMARY
+ ]
+ ];
+ $result = DBselect(DB::makeSql('interface', $options));
- $prometheus_pattern_parser = new CPrometheusPatternParser(['usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
- $prometheus_output_parser = new CPrometheusOutputParser(['usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['hostid'], $upd_item_indexes)
+ && array_key_exists($row['type'], $upd_item_indexes[$row['hostid']])) {
+ foreach ($upd_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
+ $upd_items[$i]['interfaceid'] = $row['interfaceid'];
- $required_fields = ['type', 'params', 'error_handler', 'error_handler_params'];
- $delta = false;
- $throttling = false;
- $prometheus = false;
+ unset($upd_item_indexes[$row['hostid']][$row['type']][$_i]);
+ }
- foreach ($item['preprocessing'] as $preprocessing) {
- $missing_keys = array_diff($required_fields, array_keys($preprocessing));
+ if (!$upd_item_indexes[$row['hostid']][$row['type']]) {
+ unset($upd_item_indexes[$row['hostid']][$row['type']]);
+ }
- if ($missing_keys) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item pre-processing is missing parameters: %1$s', implode(', ', $missing_keys))
- );
+ if (!$upd_item_indexes[$row['hostid']]) {
+ unset($upd_item_indexes[$row['hostid']]);
}
+ }
- if (is_array($preprocessing['type'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ if (array_key_exists($row['hostid'], $ins_item_indexes)
+ && array_key_exists($row['type'], $ins_item_indexes[$row['hostid']])) {
+ foreach ($ins_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
+ $ins_items[$i]['interfaceid'] = $row['interfaceid'];
+
+ unset($ins_item_indexes[$row['hostid']][$row['type']][$_i]);
}
- elseif ($preprocessing['type'] === '' || $preprocessing['type'] === null
- || $preprocessing['type'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'type', _('cannot be empty'))
- );
+
+ if (!$ins_item_indexes[$row['hostid']][$row['type']]) {
+ unset($ins_item_indexes[$row['hostid']][$row['type']]);
}
- if (!$type_validator->validate($preprocessing['type'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'type',
- _s('unexpected value "%1$s"', $preprocessing['type'])
- )
- );
+ if (!$ins_item_indexes[$row['hostid']]) {
+ unset($ins_item_indexes[$row['hostid']]);
}
+ }
+ }
- $preprocessing['params'] = str_replace("\r\n", "\n", $preprocessing['params']);
+ $item = null;
- switch ($preprocessing['type']) {
- case ZBX_PREPROC_MULTIPLIER:
- // Check if custom multiplier is a valid number.
- $params = $preprocessing['params'];
+ if ($upd_item_indexes) {
+ $hostid = key($upd_item_indexes);
+ $interface_type = key($upd_item_indexes[$hostid]);
+ $i = reset($upd_item_indexes[$hostid][$interface_type]);
- if (is_array($params)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($params === '' || $params === null || $params === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ $item = $upd_items[$i];
+ }
+ elseif ($ins_item_indexes) {
+ $hostid = key($ins_item_indexes);
+ $interface_type = key($ins_item_indexes[$hostid]);
+ $i = reset($ins_item_indexes[$hostid][$interface_type]);
- if (is_numeric($params)) {
- break;
- }
+ $item = $ins_items[$i];
+ }
- $types = ['usermacros' => true];
+ if ($item === null) {
+ return;
+ }
- if ($this instanceof CItemPrototype) {
- $types['lldmacros'] = true;
- }
+ $templates = DBfetchArray(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['templateid']])
+ ));
- if (!(new CMacrosResolverGeneral)->getMacroPositions($params, $types)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
- break;
-
- case ZBX_PREPROC_RTRIM:
- case ZBX_PREPROC_LTRIM:
- case ZBX_PREPROC_TRIM:
- case ZBX_PREPROC_XPATH:
- case ZBX_PREPROC_JSONPATH:
- case ZBX_PREPROC_VALIDATE_REGEX:
- case ZBX_PREPROC_VALIDATE_NOT_REGEX:
- case ZBX_PREPROC_ERROR_FIELD_JSON:
- case ZBX_PREPROC_ERROR_FIELD_XML:
- case ZBX_PREPROC_SCRIPT:
- // Check 'params' if not empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
- break;
-
- case ZBX_PREPROC_REGSUB:
- case ZBX_PREPROC_ERROR_FIELD_REGEX:
- case ZBX_PREPROC_STR_REPLACE:
- // Check if 'params' are not empty and if second parameter contains (after \n) is not empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
- $params = explode("\n", $preprocessing['params']);
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
- if ($params[0] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('first parameter is expected')
- ));
- }
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
- if (($preprocessing['type'] == ZBX_PREPROC_REGSUB
- || $preprocessing['type'] == ZBX_PREPROC_ERROR_FIELD_REGEX)
- && (!array_key_exists(1, $params) || $params[1] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- break;
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
+ }
- case ZBX_PREPROC_VALIDATE_RANGE:
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (trim($preprocessing['params']) === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $item['key_'], $templates[0]['host'],
+ $hosts[0]['host'], interfaceType2str($interface_type)
+ ));
+ }
- $params = explode("\n", $preprocessing['params']);
+ /**
+ * Inherit dependent items in nesting order.
+ *
+ * @param array $dep_items_to_link[<master item index>][<dependent item index>]
+ * @param array $items_to_link
+ * @param array $hostids
+ */
+ protected static function inheritDependentItems(array $dep_items_to_link, array $items_to_link,
+ array $hostids): void {
+ while ($dep_items_to_link) {
+ $items = [];
- if ($params[0] !== '' && !is_numeric($params[0])
- && (new CUserMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS
- && (!($this instanceof CItemPrototype)
- || ((new CLLDMacroFunctionParser())->parse($params[0]) != CParser::PARSE_SUCCESS
- && (new CLLDMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
+ foreach ($dep_items_to_link as $i => $_items) {
+ if (array_key_exists($i, $items_to_link)) {
+ $items += $_items;
+ unset($dep_items_to_link[$i]);
+ }
+ }
- if ($params[1] !== '' && !is_numeric($params[1])
- && (new CUserMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS
- && (!($this instanceof CItemPrototype)
- || ((new CLLDMacroFunctionParser())->parse($params[1]) != CParser::PARSE_SUCCESS
- && (new CLLDMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
+ static::inherit(array_values($items), [], $hostids, true);
- if (is_numeric($params[0]) && is_numeric($params[1]) && $params[0] > $params[1]) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s(
- 'Incorrect value for field "%1$s": %2$s.',
- 'params',
- _s('"%1$s" value must be less than or equal to "%2$s" value', _('min'), _('max'))
- ));
- }
- break;
-
- case ZBX_PREPROC_BOOL2DEC:
- case ZBX_PREPROC_OCT2DEC:
- case ZBX_PREPROC_HEX2DEC:
- case ZBX_PREPROC_THROTTLE_VALUE:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ $items_to_link = $items;
+ }
+ }
- if ($preprocessing['type'] == ZBX_PREPROC_THROTTLE_VALUE) {
- if ($throttling) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
- }
- else {
- $throttling = true;
- }
- }
- break;
-
- case ZBX_PREPROC_DELTA_VALUE:
- case ZBX_PREPROC_DELTA_SPEED:
- case ZBX_PREPROC_XML_TO_JSON:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array|null $hostids
+ * @param bool $is_dep_items Inherit called for dependent items.
+ */
+ abstract protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void;
- if ($preprocessing['type'] == ZBX_PREPROC_DELTA_VALUE
- || $preprocessing['type'] == ZBX_PREPROC_DELTA_SPEED) {
- // Check if one of the deltas (Delta per second or Delta value) already exists.
- if ($delta) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one change step is allowed.'));
- }
- else {
- $delta = true;
- }
- }
- break;
-
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- $api_input_rules = [
- 'type' => API_TIME_UNIT,
- 'flags' => ($this instanceof CItem)
- ? API_NOT_EMPTY | API_ALLOW_USER_MACRO
- : API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO,
- 'in' => '1:'.ZBX_MAX_TIMESHIFT
- ];
+ /**
+ * Add default values for fields that became unnecessary as the result of the change of the type fields.
+ *
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addFieldDefaultsByType(array &$items, array $db_items): void {
+ $type_field_defaults = [
+ // The fields used for multiple item types.
+ 'interfaceid' => 0,
+ 'authtype' => DB::getDefault('items', 'authtype'),
+ 'username' => DB::getDefault('items', 'username'),
+ 'password' => DB::getDefault('items', 'password'),
+ 'params' => DB::getDefault('items', 'params'),
+ 'timeout' => DB::getDefault('items', 'timeout'),
+ 'delay' => DB::getDefault('items', 'delay'),
+ 'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => 0,
+
+ // HTTP Agent item type specific fields.
+ 'url' => DB::getDefault('items', 'url'),
+ 'query_fields' => DB::getDefault('items', 'query_fields'),
+ 'request_method' => DB::getDefault('items', 'request_method'),
+ 'post_type' => DB::getDefault('items', 'post_type'),
+ 'posts' => DB::getDefault('items', 'posts'),
+ 'headers' => DB::getDefault('items', 'headers'),
+ 'status_codes' => DB::getDefault('items', 'status_codes'),
+ 'follow_redirects' => DB::getDefault('items', 'follow_redirects'),
+ 'retrieve_mode' => DB::getDefault('items', 'retrieve_mode'),
+ 'output_format' => DB::getDefault('items', 'output_format'),
+ 'http_proxy' => DB::getDefault('items', 'http_proxy'),
+ 'verify_peer' => DB::getDefault('items', 'verify_peer'),
+ 'verify_host' => DB::getDefault('items', 'verify_host'),
+ 'ssl_cert_file' => DB::getDefault('items', 'ssl_cert_file'),
+ 'ssl_key_file' => DB::getDefault('items', 'ssl_key_file'),
+ 'ssl_key_password' => DB::getDefault('items', 'ssl_key_password'),
+ 'allow_traps' => DB::getDefault('items', 'allow_traps'),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => DB::getDefault('items', 'ipmi_sensor'),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'),
+
+ // Script item type specific fields.
+ 'parameters' => [],
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => DB::getDefault('items', 'snmp_oid'),
+
+ // SSH item type specific fields.
+ 'publickey' => DB::getDefault('items', 'publickey'),
+ 'privatekey' => DB::getDefault('items', 'privatekey')
+ ];
- if (!CApiInputValidator::validate($api_input_rules, $preprocessing['params'], 'params',
- $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ $value_type_field_defaults = [
+ 'units' => DB::getDefault('items', 'units'),
+ 'trends' => DB::getDefault('items', 'trends'),
+ 'valuemapid' => 0,
+ 'logtimefmt' => DB::getDefault('items', 'logtimefmt'),
+ 'inventory_link' => DB::getDefault('items', 'inventory_link')
+ ];
- if ($throttling) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
- }
- else {
- $throttling = true;
- }
- break;
+ foreach ($items as &$item) {
+ if (!array_key_exists('type', $db_items[$item['itemid']])) {
+ continue;
+ }
- case ZBX_PREPROC_PROMETHEUS_PATTERN:
- case ZBX_PREPROC_PROMETHEUS_TO_JSON:
- if ($prometheus) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one Prometheus step is allowed.'));
- }
+ $db_item = $db_items[$item['itemid']];
- $prometheus = true;
+ if ($item['type'] != $db_item['type']) {
+ $type_field_names = CItemTypeFactory::getObject($item['type'])::FIELD_NAMES;
+ $db_type_field_names = CItemTypeFactory::getObject($db_item['type'])::FIELD_NAMES;
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
+ $field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
- if ($preprocessing['type'] == ZBX_PREPROC_PROMETHEUS_PATTERN) {
- if ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ if ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && strncmp($item['key_'], 'mqtt.get', 8) == 0) {
+ $field_names += array_flip(['delay']);
+ }
- $params = explode("\n", $preprocessing['params']);
+ if (array_intersect([$item['type'], $db_item['type']], [ITEM_TYPE_SSH, ITEM_TYPE_HTTPAGENT])) {
+ $field_names += array_flip(['authtype']);
+ }
- if ($params[0] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('first parameter is expected')
- ));
- }
- elseif (!array_key_exists(1, $params)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- elseif (!array_key_exists(2, $params)
- && ($params[1] === ZBX_PREPROC_PROMETHEUS_LABEL
- || $params[1] === ZBX_PREPROC_PROMETHEUS_FUNCTION)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('third parameter is expected')
- ));
- }
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE && array_key_exists('interfaceid', $field_names)) {
+ unset($field_names['interfaceid']);
+ }
- if ($prometheus_pattern_parser->parse($params[0]) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid Prometheus pattern')
- ));
- }
+ $item += array_intersect_key($type_field_defaults, $field_names);
+ }
+ elseif ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ if (array_key_exists('key_', $item) && $item['key_'] !== $db_item['key_']
+ && strncmp($item['key_'], 'mqtt.get', 8) == 0) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['delay']));
+ }
+ }
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ if (array_key_exists('authtype', $item) && $item['authtype'] !== $db_item['authtype']
+ && $item['authtype'] == ITEM_AUTHTYPE_PASSWORD) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['publickey', 'privatekey']));
+ }
+ }
+ elseif ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ if (array_key_exists('request_method', $item) && $item['request_method'] != $db_item['request_method']
+ && $item['request_method'] == HTTPCHECK_REQUEST_HEAD) {
+ $item += ['retrieve_mode' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS];
+ }
- if (!in_array($params[1], [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL,
- ZBX_PREPROC_PROMETHEUS_FUNCTION])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid aggregation method')
- ));
- }
+ if (array_key_exists('authtype', $item) && $item['authtype'] != $db_item['authtype']
+ && $item['authtype'] == HTTPTEST_AUTH_NONE) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['username', 'password']));
+ }
- switch ($params[1]) {
- case ZBX_PREPROC_PROMETHEUS_VALUE:
- if (array_key_exists(2, $params) && $params[2] !== '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('invalid Prometheus output')
- )
- );
- }
- break;
-
- case ZBX_PREPROC_PROMETHEUS_LABEL:
- if ($prometheus_output_parser->parse($params[2]) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('invalid Prometheus output')
- )
- );
- }
- break;
-
- case ZBX_PREPROC_PROMETHEUS_FUNCTION:
- if (!in_array($params[2], [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN,
- ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG,
- ZBX_PREPROC_PROMETHEUS_COUNT])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('unsupported Prometheus function')
- )
- );
- }
- break;
- }
- }
- // Prometheus to JSON can be empty and has only one parameter.
- elseif ($preprocessing['params'] !== '') {
- if ($prometheus_pattern_parser->parse($preprocessing['params']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid Prometheus pattern')
- ));
- }
- }
- break;
+ if (array_key_exists('allow_traps', $item) && $item['allow_traps'] != $db_item['allow_traps']
+ && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['trapper_hosts']));
+ }
+ }
- case ZBX_PREPROC_CSV_TO_JSON:
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ if (array_key_exists('value_type', $item) && $item['value_type'] != $db_item['value_type']) {
+ $type_field_names = static::VALUE_TYPE_FIELD_NAMES[$item['value_type']];
+ $db_type_field_names = static::VALUE_TYPE_FIELD_NAMES[$db_item['value_type']];
- $params = explode("\n", $preprocessing['params']);
+ $field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
- $params_cnt = count($params);
- if ($params_cnt > 3) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($params_cnt == 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- elseif ($params_cnt == 2) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('third parameter is expected')
- ));
- }
- else {
- // Correct amount of parameters, but check if they are valid.
+ if (array_key_exists('trends', $field_names)) {
+ $item += ['trends' => 0];
+ }
- if (mb_strlen($params[0]) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('value of first parameter is too long')
- ));
- }
+ $item += array_intersect_key($value_type_field_defaults, $field_names);
+ }
+ }
+ unset($item);
+ }
- if (mb_strlen($params[1]) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('value of second parameter is too long')
- ));
- }
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
+ */
+ protected static function updateParameters(array &$items, array $db_items = null,
+ array &$upd_itemids = null): void {
+ $ins_item_parameters = [];
+ $upd_item_parameters = [];
+ $del_item_parameterids = [];
- $with_header_row_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER]
- ]);
-
- if (!$with_header_row_validator->validate($params[2])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _s('value of third parameter must be one of %1$s',
- implode(', ', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])
- )
- )
- );
- }
- }
- break;
+ foreach ($items as $i => &$item) {
+ $update = false;
- case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ if ($db_items === null) {
+ if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item) && $item['parameters']) {
+ $update = true;
+ }
+ }
+ else {
+ if (!array_key_exists('type', $db_items[$item['itemid']])) {
+ continue;
+ }
- $preprocessing_types = array_column($item['preprocessing'], 'type');
+ if ($item['type'] == ITEM_TYPE_SCRIPT) {
+ if (array_key_exists('parameters', $item)) {
+ $update = true;
+ }
+ }
+ elseif ($db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT
+ && $db_items[$item['itemid']]['parameters']) {
+ $update = true;
+ }
+ }
- if (count(array_keys($preprocessing_types, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED)) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Only one not supported value check is allowed.')
- );
- }
- break;
- }
-
- switch ($preprocessing['type']) {
- case ZBX_PREPROC_RTRIM:
- case ZBX_PREPROC_LTRIM:
- case ZBX_PREPROC_TRIM:
- case ZBX_PREPROC_THROTTLE_VALUE:
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- case ZBX_PREPROC_SCRIPT:
- case ZBX_PREPROC_STR_REPLACE:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler'] != ZBX_PREPROC_FAIL_DEFAULT) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ if (!$update) {
+ continue;
+ }
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- break;
+ $changed = false;
+ $db_item_parameters = ($db_items !== null)
+ ? array_column($db_items[$item['itemid']]['parameters'], null, 'name')
+ : [];
- case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (!$unsupported_error_handler_validator->validate($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ foreach ($item['parameters'] as &$item_parameter) {
+ if (array_key_exists($item_parameter['name'], $db_item_parameters)) {
+ $db_item_parameter = $db_item_parameters[$item_parameter['name']];
+ $item_parameter['item_parameterid'] = $db_item_parameter['item_parameterid'];
+ unset($db_item_parameters[$db_item_parameter['name']]);
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE
- && $preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
- && ($preprocessing['error_handler_params'] === ''
- || $preprocessing['error_handler_params'] === null
- || $preprocessing['error_handler_params'] === false)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('cannot be empty')
- )
- );
- }
- break;
+ $upd_item_parameter = DB::getUpdatedValues('item_parameter', $item_parameter, $db_item_parameter);
- default:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (!$error_handler_validator->validate($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ if ($upd_item_parameter) {
+ $upd_item_parameters[] = [
+ 'values' => $upd_item_parameter,
+ 'where' => ['item_parameterid' => $db_item_parameter['item_parameterid']]
+ ];
+ $changed = true;
+ }
+ }
+ else {
+ $ins_item_parameters[] = ['itemid' => $item['itemid']] + $item_parameter;
+ $changed = true;
+ }
+ }
+ unset($item_parameter);
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT
- || $preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE)
- && $preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
- && ($preprocessing['error_handler_params'] === ''
- || $preprocessing['error_handler_params'] === null
- || $preprocessing['error_handler_params'] === false)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('cannot be empty')
- )
- );
- }
+ if ($db_item_parameters) {
+ $del_item_parameterids =
+ array_merge($del_item_parameterids, array_column($db_item_parameters, 'item_parameterid'));
+ $changed = true;
+ }
+
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['parameters']);
}
}
}
- }
+ unset($item);
- /**
- * Method validates preprocessing steps independently from other item properties.
- *
- * @param array $preprocessing_steps An array of item pre-processing step details.
- * See self::validateItemPreprocessing for details.
- *
- * @return bool|string
- */
- public function validateItemPreprocessingSteps(array $preprocessing_steps) {
- try {
- $this->validateItemPreprocessing(['preprocessing' => $preprocessing_steps]);
+ if ($del_item_parameterids) {
+ DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ }
- return true;
+ if ($upd_item_parameters) {
+ DB::update('item_parameter', $upd_item_parameters);
}
- catch (APIException $error) {
- return $error->getMessage();
+
+ if ($ins_item_parameters) {
+ $item_parameterids = DB::insert('item_parameter', $ins_item_parameters);
}
- }
- /**
- * Insert item pre-processing data into DB.
- *
- * @param array $items An array of items.
- * @param string $items[]['itemid']
- * @param array $items[]['preprocessing'] An array of item pre-processing data.
- */
- protected function createItemPreprocessing(array $items) {
- $item_preproc = [];
+ foreach ($items as &$item) {
+ if (!array_key_exists('parameters', $item)) {
+ continue;
+ }
- foreach ($items as $item) {
- if (array_key_exists('preprocessing', $item)) {
- $step = 1;
-
- foreach ($item['preprocessing'] as $preprocessing) {
- $item_preproc[] = [
- 'itemid' => $item['itemid'],
- 'step' => ($preprocessing['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++,
- 'type' => $preprocessing['type'],
- 'params' => $preprocessing['params'],
- 'error_handler' => $preprocessing['error_handler'],
- 'error_handler_params' => $preprocessing['error_handler_params']
- ];
+ foreach ($item['parameters'] as &$item_parameter) {
+ if (!array_key_exists('item_parameterid', $item_parameter)) {
+ $item_parameter['item_parameterid'] = array_shift($item_parameterids);
}
}
+ unset($item_parameter);
}
-
- if ($item_preproc) {
- DB::insertBatch('item_preproc', $item_preproc);
- }
+ unset($item);
}
/**
- * Update item pre-processing data in DB. Delete old records and create new ones.
- *
- * @param array $items
- * @param string $items[]['itemid']
- * @param array $items[]['preprocessing']
- * @param int $items[]['preprocessing'][]['type']
- * @param string $items[]['preprocessing'][]['params']
- * @param int $items[]['preprocessing'][]['error_handler']
- * @param string $items[]['preprocessing'][]['error_handler_params']
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
*/
- protected function updateItemPreprocessing(array $items) {
- $item_preprocs = [];
+ protected static function updatePreprocessing(array &$items, array $db_items = null,
+ array &$upd_itemids = null): void {
+ $ins_item_preprocs = [];
+ $upd_item_preprocs = [];
+ $del_item_preprocids = [];
- foreach ($items as $item) {
- if (array_key_exists('preprocessing', $item)) {
- $item_preprocs[$item['itemid']] = [];
- $step = 1;
-
- foreach ($item['preprocessing'] as $item_preproc) {
- $curr_step = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
- $item_preprocs[$item['itemid']][$curr_step] = [
- 'type' => $item_preproc['type'],
- 'params' => $item_preproc['params'],
- 'error_handler' => $item_preproc['error_handler'],
- 'error_handler_params' => $item_preproc['error_handler_params']
- ];
- }
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists('preprocessing', $item)) {
+ continue;
}
- }
- if (!$item_preprocs) {
- return;
- }
+ $changed = false;
+ $db_item_preprocs = ($db_items !== null)
+ ? array_column($db_items[$item['itemid']]['preprocessing'], null, 'step')
+ : [];
- $ins_item_preprocs = [];
- $upd_item_preprocs = [];
- $del_item_preprocids = [];
+ $step = 1;
- $options = [
- 'output' => ['item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'],
- 'filter' => ['itemid' => array_keys($item_preprocs)]
- ];
- $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+ foreach ($item['preprocessing'] as &$item_preproc) {
+ $item_preproc['step'] = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
- while ($db_item_preproc = DBfetch($db_item_preprocs)) {
- if (array_key_exists($db_item_preproc['step'], $item_preprocs[$db_item_preproc['itemid']])) {
- $item_preproc = $item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']];
- $upd_item_preproc = [];
+ if (array_key_exists($item_preproc['step'], $db_item_preprocs)) {
+ $db_item_preproc = $db_item_preprocs[$item_preproc['step']];
+ $item_preproc['item_preprocid'] = $db_item_preproc['item_preprocid'];
+ unset($db_item_preprocs[$db_item_preproc['step']]);
- if ($item_preproc['type'] != $db_item_preproc['type']) {
- $upd_item_preproc['type'] = $item_preproc['type'];
- }
- if ($item_preproc['params'] !== $db_item_preproc['params']) {
- $upd_item_preproc['params'] = $item_preproc['params'];
- }
- if ($item_preproc['error_handler'] != $db_item_preproc['error_handler']) {
- $upd_item_preproc['error_handler'] = $item_preproc['error_handler'];
- }
- if ($item_preproc['error_handler_params'] !== $db_item_preproc['error_handler_params']) {
- $upd_item_preproc['error_handler_params'] = $item_preproc['error_handler_params'];
- }
+ $upd_item_preproc = DB::getUpdatedValues('item_preproc', $item_preproc, $db_item_preproc);
- if ($upd_item_preproc) {
- $upd_item_preprocs[] = [
- 'values' => $upd_item_preproc,
- 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
- ];
+ if ($upd_item_preproc) {
+ $upd_item_preprocs[] = [
+ 'values' => $upd_item_preproc,
+ 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
+ ];
+ $changed = true;
+ }
+ }
+ else {
+ $ins_item_preprocs[] = ['itemid' => $item['itemid']] + $item_preproc;
+ $changed = true;
}
- unset($item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']]);
}
- else {
- $del_item_preprocids[] = $db_item_preproc['item_preprocid'];
+ unset($item_preproc);
+
+ if ($db_item_preprocs) {
+ $del_item_preprocids =
+ array_merge($del_item_preprocids, array_column($db_item_preprocs, 'item_preprocid'));
+ $changed = true;
}
- }
- foreach ($item_preprocs as $itemid => $preprocs) {
- foreach ($preprocs as $step => $preproc) {
- $ins_item_preprocs[] = [
- 'itemid' => $itemid,
- 'step' => $step
- ] + $preproc;
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['preprocessing']);
+ }
}
}
+ unset($item);
if ($del_item_preprocids) {
DB::delete('item_preproc', ['item_preprocid' => $del_item_preprocids]);
@@ -1947,167 +1396,277 @@ abstract class CItemGeneral extends CApiService {
}
if ($ins_item_preprocs) {
- DB::insertBatch('item_preproc', $ins_item_preprocs);
+ $item_preprocids = DB::insert('item_preproc', $ins_item_preprocs);
}
+
+ foreach ($items as &$item) {
+ if (!array_key_exists('preprocessing', $item)) {
+ continue;
+ }
+
+ foreach ($item['preprocessing'] as &$item_preproc) {
+ if (!array_key_exists('item_preprocid', $item_preproc)) {
+ $item_preproc['item_preprocid'] = array_shift($item_preprocids);
+ }
+ }
+ unset($item_preproc);
+ }
+ unset($item);
}
/**
- * Create item parameters.
- *
- * @param array $items Array of items.
- * @param array $items[]['parameters'] Item parameters.
- * @param array $items[]['parameters'][]['name'] Parameter name.
- * @param array $items[]['parameters'][]['value'] Parameter value.
- * @param array $itemids Array of item IDs that were created before.
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
*/
- protected function createItemParameters(array $items, array $itemids): void {
- $item_parameters = [];
-
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
+ protected static function updateTags(array &$items, array $db_items = null, array &$upd_itemids = null): void {
+ $ins_tags = [];
+ $del_itemtagids = [];
- if (!array_key_exists('parameters', $item) || !$item['parameters']) {
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists('tags', $item)) {
continue;
}
- foreach ($item['parameters'] as $parameter) {
- $item_parameters[] = [
- 'itemid' => $items[$key]['itemid'],
- 'name' => $parameter['name'],
- 'value' => $parameter['value']
- ];
+ $changed = false;
+ $db_tags = ($db_items !== null) ? $db_items[$item['itemid']]['tags'] : [];
+
+ foreach ($item['tags'] as &$tag) {
+ $db_itemtagid = key(array_filter($db_tags, static function (array $db_tag) use ($tag): bool {
+ return $tag['tag'] === $db_tag['tag']
+ && (!array_key_exists('value', $tag) || $tag['value'] === $db_tag['value']);
+ }));
+
+ if ($db_itemtagid !== null) {
+ $tag['itemtagid'] = $db_itemtagid;
+ unset($db_tags[$db_itemtagid]);
+ }
+ else {
+ $ins_tags[] = ['itemid' => $item['itemid']] + $tag;
+ $changed = true;
+ }
+ }
+ unset($tag);
+
+ if ($db_tags) {
+ $del_itemtagids = array_merge($del_itemtagids, array_keys($db_tags));
+ $changed = true;
}
+
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['tags']);
+ }
+ }
+ }
+ unset($item);
+
+ if ($del_itemtagids) {
+ DB::delete('item_tag', ['itemtagid' => $del_itemtagids]);
}
- if ($item_parameters) {
- DB::insertBatch('item_parameter', $item_parameters);
+ if ($ins_tags) {
+ $itemtagids = DB::insert('item_tag', $ins_tags);
}
+
+ foreach ($items as &$item) {
+ if (!array_key_exists('tags', $item)) {
+ continue;
+ }
+
+ foreach ($item['tags'] as &$tag) {
+ if (!array_key_exists('itemtagid', $tag)) {
+ $tag['itemtagid'] = array_shift($itemtagids);
+ }
+ }
+ unset($tag);
+ }
+ unset($item);
}
/**
- * Update item parameters.
- *
- * @param array $items Array of items.
- * @param int|string $items[]['itemid'] Item ID.
- * @param int|string $items[]['type'] Item type.
- * @param array $items[]['parameters'] Item parameters.
- * @param array $items[]['parameters'][]['name'] Parameter name.
- * @param array $items[]['parameters'][]['value'] Parameter value.
+ * Check for unique item keys.
+ *
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException if item keys are not unique.
*/
- protected function updateItemParameters(array $items): void {
- $db_item_parameters_by_itemid = [];
+ protected static function checkDuplicates(array $items, array $db_items = null): void {
+ $host_keys = [];
foreach ($items as $item) {
- if ($item['type'] != ITEM_TYPE_SCRIPT || array_key_exists('parameters', $item)) {
- $db_item_parameters_by_itemid[$item['itemid']] = [];
+ if ($db_items === null || $item['key_'] !== $db_items[$item['itemid']]['key_']) {
+ $host_keys[$item['hostid']][] = $item['key_'];
}
}
- if (!$db_item_parameters_by_itemid) {
+ if (!$host_keys) {
return;
}
- $options = [
- 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
- 'filter' => ['itemid' => array_keys($db_item_parameters_by_itemid)]
- ];
- $result = DBselect(DB::makeSql('item_parameter', $options));
+ $where = [];
+ foreach ($host_keys as $hostid => $keys) {
+ $where[] = '('.dbConditionId('i.hostid', [$hostid]).' AND '.dbConditionString('i.key_', $keys).')';
+ }
- while ($row = DBfetch($result)) {
- $db_item_parameters_by_itemid[$row['itemid']][$row['name']] = [
- 'item_parameterid' => $row['item_parameterid'],
- 'value' => $row['value']
- ];
+ $duplicates = DBfetchArray(DBselect(
+ 'SELECT i.key_,i.flags,h.host,h.status'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND ('.implode(' OR ', $where).')',
+ 1
+ ));
+
+ if ($duplicates) {
+ $target_is_template = ($duplicates[0]['status'] == HOST_STATUS_TEMPLATE);
+
+ switch ($duplicates[0]['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_template
+ ? _('An item with key "%1$s" already exists on the template "%2$s".')
+ : _('An item with key "%1$s" already exists on the host "%2$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_template
+ ? _('An item prototype with key "%1$s" already exists on the template "%2$s".')
+ : _('An item prototype with key "%1$s" already exists on the host "%2$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_template
+ ? _('An LLD rule with key "%1$s" already exists on the template "%2$s".')
+ : _('An LLD rule with key "%1$s" already exists on the host "%2$s".');
+ break;
+ }
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $duplicates[0]['key_'], $duplicates[0]['host']));
}
+ }
- $ins_item_parameters = [];
- $upd_item_parameters = [];
- $del_item_parameterids = [];
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected static function checkHostInterfaces(array $items, array $db_items = null): void {
+ foreach ($items as $i => &$item) {
+ $interface_type = itemTypeInterface($item['type']);
+
+ if (!in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ || $interface_type === false) {
+ unset($items[$i]);
+ continue;
+ }
- foreach ($db_item_parameters_by_itemid as $itemid => $db_item_parameters) {
- $item = $items[$itemid];
-
- if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item)) {
- foreach ($item['parameters'] as $parameter) {
- if (array_key_exists($parameter['name'], $db_item_parameters)) {
- if ($db_item_parameters[$parameter['name']]['value'] !== $parameter['value']) {
- $upd_item_parameters[] = [
- 'values' => ['value' => $parameter['value']],
- 'where' => [
- 'item_parameterid' => $db_item_parameters[$parameter['name']]['item_parameterid']
- ]
- ];
+ $check = false;
+
+ if ($db_items === null) {
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ $check = true;
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
+ }
+ }
+ }
+ else {
+ $db_item = $db_items[$item['itemid']];
+
+ if ($item['type'] == $db_item['type']) {
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0) {
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
+ }
+ }
+ }
+ else {
+ $db_interface_type = itemTypeInterface($db_item['type']);
+
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0
+ || ($interface_type != INTERFACE_TYPE_OPT
+ && $interface_type != $db_interface_type)) {
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
}
- unset($db_item_parameters[$parameter['name']]);
}
else {
- $ins_item_parameters[] = [
- 'itemid' => $itemid,
- 'name' => $parameter['name'],
- 'value' => $parameter['value']
- ];
+ if ($db_item['interfaceid'] != 0) {
+ if ($interface_type != INTERFACE_TYPE_OPT && $interface_type != $db_interface_type) {
+ $item += ['interfaceid' => $db_item['interfaceid']];
+
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1), _s('the parameter "%1$s" is missing', 'interfaceid')
+ ));
+ }
}
}
}
- $del_item_parameterids = array_merge($del_item_parameterids,
- array_column($db_item_parameters, 'item_parameterid')
- );
- }
-
- if ($del_item_parameterids) {
- DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ if (!$check) {
+ unset($items[$i]);
+ }
}
+ unset($item);
- if ($upd_item_parameters) {
- DB::update('item_parameter', $upd_item_parameters);
+ if (!$items) {
+ return;
}
- if ($ins_item_parameters) {
- DB::insertBatch('item_parameter', $ins_item_parameters);
- }
- }
+ $db_interfaces = DB::select('interface', [
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'interfaceids' => array_unique(array_column($items, 'interfaceid')),
+ 'preservekeys' => true
+ ]);
- /**
- * Check if any item from list already exists.
- * If items have item ids it will check for existing item with different itemid.
- *
- * @throw APIException
- *
- * @param array $items
- */
- protected function checkExistingItems(array $items) {
- $itemKeysByHostId = [];
- $itemIds = [];
- foreach ($items as $item) {
- if (!isset($itemKeysByHostId[$item['hostid']])) {
- $itemKeysByHostId[$item['hostid']] = [];
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['interfaceid'], $db_interfaces)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
}
- $itemKeysByHostId[$item['hostid']][] = $item['key_'];
- if (isset($item['itemid'])) {
- $itemIds[] = $item['itemid'];
+ if (bccomp($db_interfaces[$item['interfaceid']]['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('cannot be the host interface ID from another host')
+ ));
}
- }
- $sqlWhere = [];
- foreach ($itemKeysByHostId as $hostId => $keys) {
- $sqlWhere[] = '(i.hostid='.zbx_dbstr($hostId).' AND '.dbConditionString('i.key_', $keys).')';
- }
+ $interface_type = itemTypeInterface($item['type']);
- if ($sqlWhere) {
- $sql = 'SELECT i.key_,h.host'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid AND ('.implode(' OR ', $sqlWhere).')';
-
- // if we update existing items we need to exclude them from result.
- if ($itemIds) {
- $sql .= ' AND '.dbConditionInt('i.itemid', $itemIds, true);
- }
- $dbItems = DBselect($sql, 1);
- while ($dbItem = DBfetch($dbItems)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item with key "%1$s" already exists on "%2$s".', $dbItem['key_'], $dbItem['host']));
+ if ($interface_type != INTERFACE_TYPE_OPT
+ && $db_interfaces[$item['interfaceid']]['type'] != $interface_type) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid',
+ _s('the host interface ID of type "%1$s" is expected', interfaceType2str($interface_type))
+ ));
}
}
}
@@ -2222,522 +1781,499 @@ abstract class CItemGeneral extends CApiService {
}
/**
- * Validate items with type ITEM_TYPE_DEPENDENT for create or update operation.
+ * Check that dependent items of given items are valid.
*
- * @param array $items
- * @param string $items[]['itemid'] (mandatory for updated items and item prototypes)
- * @param string $items[]['hostid']
- * @param int $items[]['type']
- * @param string $items[]['master_itemid'] (mandatory for ITEM_TYPE_DEPENDENT)
- * @param int $items[]['flags'] (mandatory for items)
+ * @param array $items
+ * @param array $db_items
+ * @param bool $inherited
*
- * @throws APIException for invalid data.
+ * @throws APIException
*/
- protected function validateDependentItems(array $items) {
- $dep_items = [];
- $upd_itemids = [];
+ protected static function checkDependentItems(array $items, array $db_items = [], bool $inherited = false): void {
+ $del_links = [];
+
+ foreach ($items as $i => $item) {
+ $check = false;
- foreach ($items as $item) {
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- if ($this instanceof CDiscoveryRule || $this instanceof CItemPrototype
- || $item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- $dep_items[] = $item;
+ if (!array_key_exists('itemid', $item)) {
+ if ($item['master_itemid'] != 0) {
+ $check = true;
+ }
+ else {
+ $error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
+ }
}
+ else {
+ if (array_key_exists('master_itemid', $item)) {
+ if ($item['master_itemid'] != 0) {
+ if (bccomp($item['master_itemid'], $db_items[$item['itemid']]['master_itemid']) != 0) {
+ $check = true;
+
+ if ($db_items[$item['itemid']]['master_itemid'] != 0) {
+ $del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
+ }
+ }
+ }
+ else {
+ $error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
- if (array_key_exists('itemid', $item)) {
- $upd_itemids[] = $item['itemid'];
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
+ }
+ }
}
}
- }
-
- if (!$dep_items) {
- return;
- }
-
- if ($this instanceof CItemPrototype && $upd_itemids) {
- $db_links = DBselect(
- 'SELECT id.itemid,id.parent_itemid AS ruleid'.
- ' FROM item_discovery id'.
- ' WHERE '.dbConditionId('id.itemid', $upd_itemids)
- );
-
- $links = [];
-
- while ($db_link = DBfetch($db_links)) {
- $links[$db_link['itemid']] = $db_link['ruleid'];
+ elseif (array_key_exists('itemid', $item) && $db_items[$item['itemid']]['type'] == ITEM_TYPE_DEPENDENT) {
+ $del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
}
- foreach ($dep_items as &$dep_item) {
- if (array_key_exists('itemid', $dep_item)) {
- $dep_item['ruleid'] = $links[$dep_item['itemid']];
- }
+ if (!$check) {
+ unset($items[$i]);
}
- unset($dep_item);
}
- $master_itemids = [];
-
- foreach ($dep_items as $dep_item) {
- $master_itemids[$dep_item['master_itemid']] = true;
+ if (!$items) {
+ return;
}
- $master_items = [];
-
- // Fill relations array by master items (item prototypes). Discovery rule should not be master item.
- do {
- if ($this instanceof CItemPrototype) {
- $db_master_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
- ' FROM items i'.
- ' LEFT JOIN item_discovery id'.
- ' ON i.itemid=id.itemid'.
- ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
- );
- }
- // CDiscoveryRule, CItem
- else {
- $db_master_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.master_itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL])
- );
- }
+ if (!$inherited) {
+ self::checkMasterItems($items, $db_items);
+ }
- while ($db_master_item = DBfetch($db_master_items)) {
- $master_items[$db_master_item['itemid']] = $db_master_item;
+ $dep_item_links = self::getDependentItemLinks($items, $del_links);
- unset($master_itemids[$db_master_item['itemid']]);
- }
+ if (!$inherited && $db_items) {
+ self::checkCircularDependencies($items, $dep_item_links);
+ }
- if ($master_itemids) {
- reset($master_itemids);
+ $root_itemids = [];
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid',
- _s('Item "%1$s" does not exist or you have no access to this item', key($master_itemids))
- )
- );
+ foreach ($dep_item_links as $itemid => $master_itemid) {
+ if ($master_itemid == 0) {
+ $root_itemids[] = $itemid;
}
+ }
- $master_itemids = [];
-
- foreach ($master_items as $master_item) {
- if ($master_item['master_itemid'] != 0
- && !array_key_exists($master_item['master_itemid'], $master_items)) {
- $master_itemids[$master_item['master_itemid']] = true;
- }
- }
- } while ($master_itemids);
+ $master_item_links = self::getMasterItemLinks($items, $root_itemids, $del_links);
- foreach ($dep_items as $dep_item) {
- $master_item = $master_items[$dep_item['master_itemid']];
+ foreach ($root_itemids as $root_itemid) {
+ if (self::maxDependencyLevelExceeded($master_item_links, $root_itemid, $links_path)) {
+ [$flags, $key, $master_flags, $master_key, $is_template, $host] =
+ self::getProblemCausedItemData($links_path, $items);
- if ($dep_item['hostid'] != $master_item['hostid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('"hostid" of dependent item and master item should match')
- ));
- }
+ $error = self::getDependentItemError($flags, $master_flags, $is_template);
- if ($this instanceof CItemPrototype && $master_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
- && $dep_item['ruleid'] != $master_item['ruleid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('"ruleid" of dependent item and master item should match')
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
+ _('allowed count of dependency levels would be exceeded')
));
}
- if (array_key_exists('itemid', $dep_item)) {
- $master_itemid = $dep_item['master_itemid'];
+ if (self::maxDependentItemCountExceeded($master_item_links, $root_itemid, $links_path)) {
+ [$flags, $key, $master_flags, $master_key, $is_template, $host] =
+ self::getProblemCausedItemData($links_path, $items);
- while ($master_itemid != 0) {
- if ($master_itemid == $dep_item['itemid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('circular item dependency is not allowed')
- ));
- }
+ $error = self::getDependentItemError($flags, $master_flags, $is_template);
- $master_itemid = $master_items[$master_itemid]['master_itemid'];
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
+ _('allowed count of dependent items would be exceeded')
+ ));
}
}
+ }
- // Fill relations array by dependent items (item prototypes).
- $root_itemids = [];
+ /**
+ * Check that master item IDs of given dependent items are valid.
+ *
+ * @param array $items
+ * @param array $db_items
+ *
+ * @throws APIException
+ */
+ private static function checkMasterItems(array $items, array $db_items): void {
+ $master_itemids = array_unique(array_column($items, 'master_itemid'));
+ $flags = $items[key($items)]['flags'];
- foreach ($master_items as $master_item) {
- if ($master_item['master_itemid'] == 0) {
- $root_itemids[] = $master_item['itemid'];
- }
+ if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $db_master_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
+ ' FROM items i'.
+ ' LEFT JOIN item_discovery id ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('i.itemid', $master_itemids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
+ ), 'itemid');
}
-
- $dependent_items = [];
-
- foreach ($dep_items as $dep_item) {
- if (array_key_exists('itemid', $dep_item)) {
- $dependent_items[$dep_item['master_itemid']][] = $dep_item['itemid'];
- }
+ else {
+ $db_master_items = DB::select('items', [
+ 'output' => ['itemid', 'hostid', 'master_itemid'],
+ 'itemids' => $master_itemids,
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL
+ ],
+ 'preservekeys' => true
+ ]);
}
- $master_itemids = $root_itemids;
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['master_itemid'], $db_master_items)) {
+ $error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
- do {
- $sql = 'SELECT i.master_itemid,i.itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.master_itemid', $master_itemids);
- if ($upd_itemids) {
- $sql .= ' AND '.dbConditionId('i.itemid', $upd_itemids, true); // Exclude updated items.
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
}
- $db_items = DBselect($sql);
+ $db_master_item = $db_master_items[$item['master_itemid']];
- while ($db_item = DBfetch($db_items)) {
- $dependent_items[$db_item['master_itemid']][] = $db_item['itemid'];
+ if (bccomp($db_master_item['hostid'], $item['hostid']) != 0) {
+ $error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('cannot be an item/item prototype ID from another host or template')
+ : _('cannot be an item ID from another host or template');
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
}
- $_master_itemids = $master_itemids;
- $master_itemids = [];
+ if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE && $db_master_item['ruleid'] != 0) {
+ $item_ruleid = array_key_exists('itemid', $item)
+ ? $db_items[$item['itemid']]['ruleid']
+ : $item['ruleid'];
- foreach ($_master_itemids as $master_itemid) {
- if (array_key_exists($master_itemid, $dependent_items)) {
- $master_itemids = array_merge($master_itemids, $dependent_items[$master_itemid]);
+ if (bccomp($db_master_item['ruleid'], $item_ruleid) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', _('cannot be an item prototype ID from another LLD rule')
+ ));
}
}
- } while ($master_itemids);
-
- foreach ($dep_items as $dep_item) {
- if (!array_key_exists('itemid', $dep_item)) {
- $dependent_items[$dep_item['master_itemid']][] = false;
- }
- }
-
- foreach ($root_itemids as $root_itemid) {
- self::checkDependencyDepth($dependent_items, $root_itemid);
}
}
/**
- * Validate depth and amount of elements in the tree of the dependent items.
+ * Get dependent item links starting from the given dependent items and till the highest dependency level.
*
- * @param array $dependent_items
- * @param string $dependent_items[<master_itemid>][] List if the dependent item IDs ("false" for new items)
- * by master_itemid.
- * @param string $root_itemid ID of the item being checked.
- * @param int $level Current dependency level.
+ * @param array $items
+ * @param array $del_links
*
- * @throws APIException for invalid data.
+ * @return array Array of the links where each key contain the ID of dependent item and value contain the
+ * appropriate ID of the master item.
*/
- private static function checkDependencyDepth(array $dependent_items, $root_itemid, $level = 0) {
- $count = 0;
-
- if (array_key_exists($root_itemid, $dependent_items)) {
- if (++$level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('maximum number of dependency levels reached')
- ));
- }
+ private static function getDependentItemLinks(array $items, array $del_links): array {
+ $links = array_column($items, 'master_itemid', 'itemid');
+ $master_itemids = array_flip(array_column($items, 'master_itemid'));
+
+ while ($master_itemids) {
+ $options = [
+ 'output' => ['itemid', 'hostid', 'master_itemid'],
+ 'itemids' => array_keys($master_itemids)
+ ];
+ $db_master_items = DBselect(DB::makeSql('items', $options));
- foreach ($dependent_items[$root_itemid] as $master_itemid) {
- $count++;
+ $master_itemids = [];
- if ($master_itemid !== false) {
- $count += self::checkDependencyDepth($dependent_items, $master_itemid, $level);
+ while ($db_master_item = DBfetch($db_master_items)) {
+ if (array_key_exists($db_master_item['itemid'], $del_links)
+ && bccomp($db_master_item['master_itemid'], $del_links[$db_master_item['itemid']]) == 0) {
+ $links[$db_master_item['itemid']] = 0;
+ continue;
}
- }
- if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('maximum dependent items count reached')
- ));
+ $links[$db_master_item['itemid']] = $db_master_item['master_itemid'];
+
+ if ($db_master_item['master_itemid'] != 0) {
+ $master_itemids[$db_master_item['master_itemid']] = true;
+ }
}
}
- return $count;
+ return $links;
}
/**
- * Converts headers field text to hash with header name as key.
+ * Check that the changed master item IDs of dependent items do not create a circular dependencies.
*
- * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ * @param array $items
+ * @param array $dep_item_links
*
- * @return array
+ * @throws APIException
*/
- protected function headersStringToArray($headers) {
- $result = [];
-
- foreach (explode("\r\n", $headers) as $header) {
- $header = explode(': ', $header, 2);
+ private static function checkCircularDependencies(array $items, array $dep_item_links): void {
+ foreach ($items as $i => $item) {
+ $master_itemid = $item['master_itemid'];
+
+ while ($master_itemid != 0) {
+ if (bccomp($master_itemid, $item['itemid']) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', _('circular item dependency is not allowed')
+ ));
+ }
- if (count($header) == 2) {
- $result[$header[0]] = $header[1];
+ $master_itemid = $dep_item_links[$master_itemid];
}
}
-
- return $result;
}
/**
- * Converts headers fields hash to string.
+ * Get master item links starting from the given master items and till the lowest level master items.
*
- * @param array $headers Array of headers where key is header name.
+ * @param array $items
+ * @param array $master_itemids
+ * @param array $del_links
*
- * @return string
+ * @return array Array of the links where each key contain the ID of master item and value contain the array of
+ * appropriate dependent item IDs.
*/
- protected function headersArrayToString(array $headers) {
- $result = [];
+ private static function getMasterItemLinks(array $items, array $master_itemids, array $del_links): array {
+ $ins_links = [];
+ $upd_item_links = [];
- foreach ($headers as $k => $v) {
- $result[] = $k.': '.$v;
+ foreach ($items as $item) {
+ if (array_key_exists('itemid', $item)) {
+ $upd_item_links[$item['master_itemid']][] = $item['itemid'];
+ }
+ else {
+ $ins_links[$item['master_itemid']][] = 0;
+ }
}
- return implode("\r\n", $result);
+ $links = [];
+
+ do {
+ $options = [
+ 'output' => ['master_itemid', 'itemid'],
+ 'filter' => [
+ 'master_itemid' => $master_itemids
+ ]
+ ];
+ $db_items = DBselect(DB::makeSql('items', $options));
+
+ $_master_itemids = [];
+
+ while ($db_item = DBfetch($db_items)) {
+ if (array_key_exists($db_item['itemid'], $del_links)
+ && bccomp($db_item['master_itemid'], $del_links[$db_item['itemid']]) == 0) {
+ continue;
+ }
+
+ $links[$db_item['master_itemid']][] = $db_item['itemid'];
+ $_master_itemids[] = $db_item['itemid'];
+ }
+
+ foreach ($master_itemids as $master_itemid) {
+ if (array_key_exists($master_itemid, $upd_item_links)) {
+ foreach ($upd_item_links[$master_itemid] as $itemid) {
+ $_master_itemids[] = $itemid;
+ $links[$master_itemid][] = $itemid;
+ }
+ }
+ }
+
+ $master_itemids = $_master_itemids;
+ } while ($master_itemids);
+
+ foreach ($ins_links as $master_itemid => $ins_items) {
+ $links[$master_itemid] = array_key_exists($master_itemid, $links)
+ ? array_merge($links[$master_itemid], $ins_items)
+ : $ins_items;
+ }
+
+ return $links;
}
/**
- * Validate item with type ITEM_TYPE_HTTPAGENT.
+ * Check whether maximum number of dependency levels is exceeded.
*
- * @param array $item Array of item fields.
- * @param array $db_item Array of item database fields for update action or empty array for create action.
+ * @param array $master_item_links
+ * @param string $master_itemid
+ * @param array|null $links_path
+ * @param int $level
*
- * @throws APIException for invalid data.
+ * @return bool
*/
- protected function validateHTTPCheck(array $item, array $db_item) {
- $rules = [
- 'timeout' => [
- 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
- ? API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
- : API_NOT_EMPTY | API_ALLOW_USER_MACRO,
- 'in' => '1:'.SEC_PER_MIN
- ],
- 'url' => [
- 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY,
- 'length' => DB::getFieldLength('items', 'url')
- ],
- 'status_codes' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'status_codes')
- ],
- 'follow_redirects' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])
- ],
- 'post_type' => [
- 'type' => API_INT32,
- 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])
- ],
- 'http_proxy' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')
- ],
- 'headers' => [
- 'type' => API_STRINGS_UTF8
- ],
- 'retrieve_mode' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
- HTTPTEST_STEP_RETRIEVE_MODE_BOTH
- ])
- ],
- 'request_method' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD
- ])
- ],
- 'output_format' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])
- ],
- 'allow_traps' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])
- ],
- 'ssl_cert_file' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')
- ],
- 'ssl_key_file' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')
- ],
- 'ssl_key_password' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')
- ],
- 'verify_peer' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])
- ],
- 'verify_host' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])
- ],
- 'authtype' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
- HTTPTEST_AUTH_DIGEST
- ])
- ]
- ];
-
- $data = $item + $db_item;
-
- if (array_key_exists('authtype', $data)
- && ($data['authtype'] == HTTPTEST_AUTH_BASIC || $data['authtype'] == HTTPTEST_AUTH_NTLM
- || $data['authtype'] == HTTPTEST_AUTH_KERBEROS || $data['authtype'] == HTTPTEST_AUTH_DIGEST)) {
- $rules += [
- 'username' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
- 'password' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')]
- ];
+ private static function maxDependencyLevelExceeded(array $master_item_links, string $master_itemid,
+ array &$links_path = null, int $level = 0): bool {
+ if (!array_key_exists($master_itemid, $master_item_links)) {
+ return false;
}
- // Strict validation for 'retrieve_mode' only for create action.
- if (array_key_exists('request_method', $data) && $data['request_method'] == HTTPCHECK_REQUEST_HEAD
- && array_key_exists('retrieve_mode', $item)) {
- $rules['retrieve_mode']['in'] = (string) HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ if ($links_path === null) {
+ $links_path = [];
}
- if (array_key_exists('post_type', $data)
- && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
- $rules['posts'] = [
- 'type' => API_STRING_UTF8,
- 'length' => DB::getFieldLength('items', 'posts')
- ];
+ $links_path[] = $master_itemid;
+ $level++;
+
+ if ($level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
+ return true;
}
- if (array_key_exists('templateid', $data) && $data['templateid']) {
- $rules['interfaceid'] = [
- 'type' => API_ID, 'flags' => API_REQUIRED
- ];
+ foreach ($master_item_links[$master_itemid] as $itemid) {
+ $_links_path = $links_path;
+
+ if (self::maxDependencyLevelExceeded($master_item_links, $itemid, $_links_path, $level)) {
+ $links_path = $_links_path;
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- unset($rules['interfaceid']['flags']);
+ return true;
}
}
- if (array_key_exists('trapper_hosts', $item) && $item['trapper_hosts'] !== ''
- && (!array_key_exists('allow_traps', $data) || $data['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', _('should be empty'))
- );
- }
+ return false;
+ }
- // Keep values only for fields with defined validation rules.
- $data = array_intersect_key($data, $rules);
- if (!CApiInputValidator::validate(['type' => API_OBJECT, 'fields' => $rules], $data, '', $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ /**
+ * Check whether maximum count of dependent items is exceeded.
+ *
+ * @param array $master_item_links
+ * @param string $master_itemid
+ * @param array|null $links_path
+ * @param int $count
+ *
+ * @return bool
+ */
+ private static function maxDependentItemCountExceeded(array $master_item_links, string $master_itemid,
+ array &$links_path = null, int &$count = 0): bool {
+ if (!array_key_exists($master_itemid, $master_item_links)) {
+ return false;
}
- if (array_key_exists('query_fields', $item)) {
- if (!is_array($item['query_fields'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('an array is expected'))
- );
- }
+ if ($links_path === null) {
+ $links_path = [];
+ }
- foreach ($item['query_fields'] as $v) {
- if (!is_array($v) || count($v) > 1 || key($v) === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('nonempty key and value pair expected'))
- );
- }
- }
+ $links_path[] = $master_itemid;
+ $count += count($master_item_links[$master_itemid]);
- if (strlen(json_encode($item['query_fields'])) > DB::getFieldLength('items', 'query_fields')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'query_fields',
- _('cannot convert to JSON, result value too long')
- ));
- }
+ if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
+ return true;
}
- if (array_key_exists('headers', $item)) {
- if (!is_array($item['headers'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'headers', _('an array is expected'))
- );
- }
+ foreach ($master_item_links[$master_itemid] as $itemid) {
+ $_links_path = $links_path;
- foreach ($item['headers'] as $k => $v) {
- if (trim($k) === '' || !is_string($v) || $v === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'headers', _('nonempty key and value pair expected'))
- );
- }
+ if (self::maxDependentItemCountExceeded($master_item_links, $itemid, $_links_path, $count)) {
+ $links_path = $_links_path;
+
+ return true;
}
}
- if (array_key_exists('status_codes', $item) && $item['status_codes']) {
- $ranges_parser = new CRangesParser([
- 'usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
+ return false;
+ }
- if ($ranges_parser->parse($item['status_codes']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value "%1$s" for "%2$s" field.', $item['status_codes'], 'status_codes')
- );
+ /**
+ * Get data for a dependent item that causes a problem, based on the given path where the problem was detected.
+ *
+ * @param array $links_path
+ * @param array $items
+ *
+ * @return array
+ */
+ private static function getProblemCausedItemData(array $links_path, array $items): array {
+ foreach ($items as $item) {
+ if (in_array($item['master_itemid'], $links_path)) {
+ break;
}
}
- if ((array_key_exists('post_type', $item) || array_key_exists('posts', $item))
- && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
- $posts = array_key_exists('posts', $data) ? $data['posts'] : '';
- libxml_use_internal_errors(true);
-
- if ($data['post_type'] == ZBX_POSTTYPE_XML
- && simplexml_load_string($posts, null, LIBXML_IMPORT_FLAGS) === false) {
- $errors = libxml_get_errors();
- libxml_clear_errors();
+ $master_item_data = DBfetch(DBselect(
+ 'SELECT i.flags,i.key_,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['master_itemid']])
+ ));
- if (!$errors) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('XML is expected'))
- );
- }
- else {
- $error = reset($errors);
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'posts',
- _s('%1$s [Line: %2$s | Column: %3$s]', '('.$error->code.') '.trim($error->message),
- $error->line, $error->column
- )));
- }
- }
+ $flags = $item['flags'];
+ $key = $item['key_'];
+ $master_flags = $master_item_data['flags'];
+ $master_key = $master_item_data['key_'];
+ $is_template = $item['host_status'] == HOST_STATUS_TEMPLATE;
+ $host = $master_item_data['host'];
- if ($data['post_type'] == ZBX_POSTTYPE_JSON) {
- if (trim($posts, " \r\n") === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
- );
- }
+ return [$flags, $key, $master_flags, $master_key, $is_template, $host];
+ }
- $types = [
- 'usermacros' => true,
- 'macros_n' => [
- '{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}',
- '{ITEM.KEY}'
- ]
- ];
+ /**
+ * Get the error message about problem with dependent item according to given data.
+ *
+ * @param int $flags
+ * @param int $master_flags
+ * @param bool $is_template
+ *
+ * @return string
+ */
+ private static function getDependentItemError(int $flags, int $master_flags, bool $is_template): string {
+ if ($flags == ZBX_FLAG_DISCOVERY_NORMAL) {
+ return $is_template
+ ? _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ elseif ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ if ($master_flags == ZBX_FLAG_DISCOVERY_NORMAL) {
+ return $is_template
+ ? _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ else {
+ return $is_template
+ ? _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ }
+ elseif ($flags == ZBX_FLAG_DISCOVERY_RULE) {
+ return $is_template
+ ? _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ }
- if ($this instanceof CItemPrototype) {
- $types['lldmacros'] = true;
- }
+ /**
+ * Converts headers field text to hash with header name as key.
+ *
+ * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ *
+ * @return array
+ */
+ protected static function headersStringToArray(string $headers): array {
+ $result = [];
- $matches = (new CMacrosResolverGeneral)->getMacroPositions($posts, $types);
+ foreach (explode("\r\n", $headers) as $header) {
+ $header = explode(': ', $header, 2);
- $shift = 0;
+ if (count($header) == 2) {
+ $result[$header[0]] = $header[1];
+ }
+ }
- foreach ($matches as $pos => $substr) {
- $posts = substr_replace($posts, '1', $pos + $shift, strlen($substr));
- $shift = $shift + 1 - strlen($substr);
- }
+ return $result;
+ }
- json_decode($posts);
+ /**
+ * Converts headers fields hash to string.
+ *
+ * @param array $headers Array of headers where key is header name.
+ *
+ * @return string
+ */
+ protected static function headersArrayToString(array $headers): string {
+ $result = [];
- if (json_last_error()) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
- );
- }
- }
+ foreach ($headers as $k => $v) {
+ $result[] = $k.': '.$v;
}
+
+ return implode("\r\n", $result);
}
/**
@@ -2812,162 +2348,254 @@ abstract class CItemGeneral extends CApiService {
}
/**
- * Update item tags.
+ * Check that valuemap belong to same host as item.
*
- * @param array $items
- * @param string $items[]['itemid']
- * @param array $items[]['tags']
- * @param string $items[]['tags'][]['tag']
- * @param string $items[]['tags'][]['value']
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
*/
- protected function updateItemTags(array $items): void {
- $items = array_filter($items, function ($item) {
- return array_key_exists('tags', $item);
- });
-
- // Select tags from database.
- $db_tags = DBselect(
- 'SELECT itemtagid, itemid, tag, value'.
- ' FROM item_tag'.
- ' WHERE '.dbConditionInt('itemid', array_keys($items))
- );
+ protected static function checkValueMaps(array $items, array $db_items = null): void {
+ $item_indexes = [];
- array_walk($items, function (&$item) {
- $item['db_tags'] = [];
- });
+ foreach ($items as $i => $item) {
+ if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0
+ && ($db_items === null
+ || bccomp($item['valuemapid'], $db_items[$item['itemid']]['valuemapid']) != 0)) {
+ $item_indexes[$item['valuemapid']][] = $i;
+ }
+ }
- while ($db_tag = DBfetch($db_tags)) {
- $items[$db_tag['itemid']]['db_tags'][] = $db_tag;
+ if (!$item_indexes) {
+ return;
}
- // Find which tags must be added/deleted.
- $new_tags = [];
- $del_tagids = [];
- foreach ($items as $item) {
- CArrayHelper::sort($item['tags'], ['tag', 'value']);
+ $options = [
+ 'output' => ['valuemapid', 'hostid'],
+ 'valuemapids' => array_keys($item_indexes)
+ ];
+ $db_valuemaps = DBselect(DB::makeSql('valuemap', $options));
- foreach ($item['db_tags'] as $del_tag_key => $tag_delete) {
- foreach ($item['tags'] as $new_tag_key => $tag_add) {
- if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
- unset($item['db_tags'][$del_tag_key], $item['tags'][$new_tag_key]);
- continue 2;
- }
+ while ($db_valuemap = DBfetch($db_valuemaps)) {
+ foreach ($item_indexes[$db_valuemap['valuemapid']] as $i) {
+ if (bccomp($db_valuemap['hostid'], $items[$i]['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/valuemapid', _('cannot be a value map ID from another host or template')
+ ));
}
}
+ }
+ }
+
+ /**
+ * Add the internally used fields to the given $db_items.
+ *
+ * @param array $db_items
+ */
+ protected static function addInternalFields(array &$db_items): void {
+ $result = DBselect(
+ 'SELECT i.itemid,i.hostid,i.templateid,i.flags,h.status AS host_status'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', array_keys($db_items))
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_items[$row['itemid']] += $row;
+ }
+ }
+
+ /**
+ * Note: instances may override this to add e.g. tags.
+ *
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addAffectedObjects(array $items, array &$db_items): void {
+ self::addAffectedTags($items, $db_items);
+ self::addAffectedPreprocessing($items, $db_items);
+ self::addAffectedParameters($items, $db_items);
+ }
- $del_tagids = array_merge($del_tagids, array_column($item['db_tags'], 'itemtagid'));
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addAffectedTags(array $items, array &$db_items): void {
+ $itemids = [];
- foreach ($item['tags'] as $tag_add) {
- $tag_add['itemid'] = $item['itemid'];
- $new_tags[] = $tag_add;
+ foreach ($items as $item) {
+ if (array_key_exists('tags', $item)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['tags'] = [];
}
}
- if ($del_tagids) {
- DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ if (!$itemids) {
+ return;
}
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
+
+ $options = [
+ 'output' => ['itemtagid', 'itemid', 'tag', 'value'],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_tags = DBselect(DB::makeSql('item_tag', $options));
+
+ while ($db_item_tag = DBfetch($db_item_tags)) {
+ $db_items[$db_item_tag['itemid']]['tags'][$db_item_tag['itemtagid']] =
+ array_diff_key($db_item_tag, array_flip(['itemid']));
}
}
/**
- * Record item tags into database.
- *
- * @param array $items
- * @param array $items[]['tags']
- * @param string $items[]['tags'][]['tag']
- * @param string $items[]['tags'][]['value']
- * @param int $items[]['itemid']
+ * @param array $items
+ * @param array $db_items
*/
- protected function createItemTags(array $items): void {
- $new_tags = [];
- foreach ($items as $key => $item) {
- if (array_key_exists('tags', $item)) {
- foreach ($item['tags'] as $tag) {
- $tag['itemid'] = $item['itemid'];
- $new_tags[] = $tag;
- }
+ protected static function addAffectedPreprocessing(array $items, array &$db_items): void {
+ $itemids = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['preprocessing'] = [];
}
}
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
+ if (!$itemids) {
+ return;
+ }
+
+ $options = [
+ 'output' => [
+ 'item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'
+ ],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+
+ while ($db_item_preproc = DBfetch($db_item_preprocs)) {
+ $db_items[$db_item_preproc['itemid']]['preprocessing'][$db_item_preproc['item_preprocid']] =
+ array_diff_key($db_item_preproc, array_flip(['itemid']));
}
}
/**
- * Check that valuemap belong to same host as item.
- *
* @param array $items
+ * @param array $db_items
*/
- protected function validateValueMaps(array $items): void {
- $valuemapids_by_hostid = [];
+ protected static function addAffectedParameters(array $items, array &$db_items): void {
+ $itemids = [];
foreach ($items as $item) {
- if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0) {
- $valuemapids_by_hostid[$item['hostid']][$item['valuemapid']] = true;
+ $db_type = $db_items[$item['itemid']]['type'];
+
+ if ((array_key_exists('parameters', $item) && $item['type'] == ITEM_TYPE_SCRIPT)
+ || ($item['type'] != $db_type && $db_type == ITEM_TYPE_SCRIPT)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['parameters'] = [];
+ }
+ elseif (array_key_exists('parameters', $item)) {
+ $db_items[$item['itemid']]['parameters'] = [];
}
}
- $sql_where = [];
- foreach ($valuemapids_by_hostid as $hostid => $valuemapids) {
- $sql_where[] = '(vm.hostid='.zbx_dbstr($hostid).' AND '.
- dbConditionId('vm.valuemapid', array_keys($valuemapids)).')';
+ if (!$itemids) {
+ return;
}
- if ($sql_where) {
- $result = DBselect(
- 'SELECT vm.valuemapid,vm.hostid'.
- ' FROM valuemap vm'.
- ' WHERE '.implode(' OR ', $sql_where)
- );
+ $options = [
+ 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_parameters = DBselect(DB::makeSql('item_parameter', $options));
+
+ while ($db_item_parameter = DBfetch($db_item_parameters)) {
+ $db_items[$db_item_parameter['itemid']]['parameters'][$db_item_parameter['item_parameterid']] =
+ array_diff_key($db_item_parameter, array_flip(['itemid']));
+ }
+ }
+
+ /**
+ * Add the inherited items of the given items to the given item array.
+ *
+ * @param array $db_items
+ */
+ public static function addInheritedItems(array &$db_items): void {
+ $templateids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name'],
+ 'filter' => ['templateid' => $templateids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $templateids = [];
+
while ($row = DBfetch($result)) {
- unset($valuemapids_by_hostid[$row['hostid']][$row['valuemapid']]);
+ if (!array_key_exists($row['itemid'], $db_items)) {
+ $templateids[] = $row['itemid'];
- if (!$valuemapids_by_hostid[$row['hostid']]) {
- unset($valuemapids_by_hostid[$row['hostid']]);
+ $db_items[$row['itemid']] = $row;
}
}
+ } while ($templateids);
+ }
- if ($valuemapids_by_hostid) {
- $hostid = key($valuemapids_by_hostid);
- $valuemapid = key($valuemapids_by_hostid[$hostid]);
+ /**
+ * Reset the MIN and MAX values of Y axis in the graphs, if such are calculated using the given items.
+ *
+ * @param array $del_itemids
+ */
+ protected static function resetGraphsYAxis(array $del_itemids): void {
+ DB::update('graphs', [
+ 'values' => [
+ 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
+ 'ymin_itemid' => null
+ ],
+ 'where' => ['ymin_itemid' => $del_itemids]
+ ]);
- $host_row = DBfetch(DBselect('SELECT h.host FROM hosts h WHERE h.hostid='.zbx_dbstr($hostid)));
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Valuemap with ID "%1$s" is not available on "%2$s".',
- $valuemapid, $host_row['host']
- ));
- }
- }
+ DB::update('graphs', [
+ 'values' => [
+ 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
+ 'ymax_itemid' => null
+ ],
+ 'where' => ['ymax_itemid' => $del_itemids]
+ ]);
}
/**
- * Normalize preprocessing step parameters.
- *
- * @param array $preprocessing Preprocessing steps.
- * @param string $preprocessing[<num>]['params'] Preprocessing step parameters.
- * @param int $preprocessing[<num>]['type'] Preprocessing step type.
+ * Delete triggers and trigger prototypes, which contain the given items in the expression.
*
- * @return array
+ * @param array $del_itemids
*/
- protected function normalizeItemPreprocessingSteps(array $preprocessing): array {
- foreach ($preprocessing as &$step) {
- $step['params'] = str_replace("\r\n", "\n", $step['params']);
- $params = explode("\n", $step['params']);
-
- switch ($step['type']) {
- case ZBX_PREPROC_PROMETHEUS_PATTERN:
- if (!array_key_exists(2, $params)) {
- $params[2] = '';
- }
- break;
+ protected static function deleteAffectedTriggers(array $del_itemids): void {
+ $result = DBselect(
+ 'SELECT DISTINCT f.triggerid,t.flags'.
+ ' FROM functions f,triggers t'.
+ ' WHERE f.triggerid=t.triggerid'.
+ ' AND '.dbConditionInt('f.itemid', $del_itemids)
+ );
+
+ $del_trigger_prototypeids = [];
+ $del_triggerids = [];
+
+ while ($row = DBfetch($result)) {
+ if ($row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $del_trigger_prototypeids[] = $row['triggerid'];
}
+ else {
+ $del_triggerids[] = $row['triggerid'];
+ }
+ }
- $step['params'] = implode("\n", $params);
+ if ($del_triggerids) {
+ CTriggerManager::delete($del_triggerids);
}
- unset($step);
- return $preprocessing;
+ if ($del_trigger_prototypeids) {
+ CTriggerPrototypeManager::delete($del_trigger_prototypeids);
+ }
}
}
diff --git a/ui/include/classes/api/services/CItemGeneralOld.php b/ui/include/classes/api/services/CItemGeneralOld.php
new file mode 100644
index 00000000000..9fbb02de63c
--- /dev/null
+++ b/ui/include/classes/api/services/CItemGeneralOld.php
@@ -0,0 +1,2973 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2022 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 containing methods for operations with item general.
+ */
+abstract class CItemGeneralOld extends CApiService {
+
+ public const ACCESS_RULES = [
+ 'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
+ 'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
+ 'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
+ 'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
+ ];
+
+ public const INTERFACE_TYPES_BY_PRIORITY = [
+ INTERFACE_TYPE_AGENT,
+ INTERFACE_TYPE_SNMP,
+ INTERFACE_TYPE_JMX,
+ INTERFACE_TYPE_IPMI
+ ];
+
+ const ERROR_EXISTS_TEMPLATE = 'existsTemplate';
+ const ERROR_EXISTS = 'exists';
+ const ERROR_NO_INTERFACE = 'noInterface';
+ const ERROR_INVALID_KEY = 'invalidKey';
+
+ protected $fieldRules;
+
+ /**
+ * @abstract
+ *
+ * @param array $options
+ *
+ * @return array
+ */
+ abstract public function get($options = []);
+
+ public function __construct() {
+ parent::__construct();
+
+ // template - if templated item, value is taken from template item, cannot be changed on host
+ // system - values should not be updated
+ // host - value should be null for template items
+ $this->fieldRules = [
+ 'type' => ['template' => 1],
+ 'snmp_oid' => ['template' => 1],
+ 'hostid' => [],
+ 'name' => ['template' => 1],
+ 'description' => [],
+ 'key_' => ['template' => 1],
+ 'master_itemid' => ['template' => 1],
+ 'delay' => [],
+ 'history' => [],
+ 'trends' => [],
+ 'status' => [],
+ 'discover' => [],
+ 'value_type' => ['template' => 1],
+ 'trapper_hosts' => [],
+ 'units' => ['template' => 1],
+ 'formula' => ['template' => 1],
+ 'error' => ['system' => 1],
+ 'lastlogsize' => ['system' => 1],
+ 'logtimefmt' => [],
+ 'templateid' => ['system' => 1],
+ 'valuemapid' => ['template' => 1],
+ 'params' => [],
+ 'ipmi_sensor' => ['template' => 1],
+ 'authtype' => [],
+ 'username' => [],
+ 'password' => [],
+ 'publickey' => [],
+ 'privatekey' => [],
+ 'mtime' => ['system' => 1],
+ 'flags' => [],
+ 'filter' => [],
+ 'interfaceid' => ['host' => 1],
+ 'inventory_link' => [],
+ 'lifetime' => [],
+ 'preprocessing' => ['template' => 1],
+ 'overrides' => ['template' => 1],
+ 'jmx_endpoint' => [],
+ 'url' => ['template' => 1],
+ 'timeout' => ['template' => 1],
+ 'query_fields' => ['template' => 1],
+ 'parameters' => ['template' => 1],
+ 'posts' => ['template' => 1],
+ 'status_codes' => ['template' => 1],
+ 'follow_redirects' => ['template' => 1],
+ 'post_type' => ['template' => 1],
+ 'http_proxy' => ['template' => 1],
+ 'headers' => ['template' => 1],
+ 'retrieve_mode' => ['template' => 1],
+ 'request_method' => ['template' => 1],
+ 'output_format' => ['template' => 1],
+ 'allow_traps' => [],
+ 'ssl_cert_file' => ['template' => 1],
+ 'ssl_key_file' => ['template' => 1],
+ 'ssl_key_password' => ['template' => 1],
+ 'verify_peer' => ['template' => 1],
+ 'verify_host' => ['template' => 1]
+ ];
+
+ $this->errorMessages = array_merge($this->errorMessages, [
+ self::ERROR_NO_INTERFACE => _('Cannot find host interface on "%1$s" for item key "%2$s".')
+ ]);
+ }
+
+ /**
+ * Check items data.
+ *
+ * Any system field passed to the function will be unset.
+ *
+ * @throw APIException
+ *
+ * @param array $items passed by reference
+ * @param bool $update
+ */
+ protected function checkInput(array &$items, $update = false) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_ITEM_TYPES)]
+ ]];
+ if ($update) {
+ unset($api_input_rules['fields']['type']['flags']);
+ }
+
+ foreach ($items as $num => $item) {
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($num + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ if ($update) {
+ $itemDbFields = ['itemid' => null];
+
+ $dbItemsFields = ['itemid', 'templateid'];
+ foreach ($this->fieldRules as $field => $rule) {
+ if (!isset($rule['system'])) {
+ $dbItemsFields[] = $field;
+ }
+ }
+
+ $dbItems = $this->get([
+ 'output' => $dbItemsFields,
+ 'itemids' => zbx_objectValues($items, 'itemid'),
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+
+ $dbHosts = API::Host()->get([
+ 'output' => ['hostid', 'status', 'name'],
+ 'hostids' => zbx_objectValues($dbItems, 'hostid'),
+ 'templated_hosts' => true,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+ }
+ else {
+ $itemDbFields = [
+ 'name' => null,
+ 'key_' => null,
+ 'hostid' => null,
+ 'type' => null,
+ 'value_type' => null,
+ 'delay' => null
+ ];
+
+ $dbHosts = API::Host()->get([
+ 'output' => ['hostid', 'status', 'name'],
+ 'hostids' => zbx_objectValues($items, 'hostid'),
+ 'templated_hosts' => true,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+
+ $discovery_rules = [];
+
+ if ($this instanceof CItemPrototype) {
+ $itemDbFields['ruleid'] = null;
+ $druleids = zbx_objectValues($items, 'ruleid');
+
+ if ($druleids) {
+ $discovery_rules = API::DiscoveryRule()->get([
+ 'output' => ['hostid'],
+ 'itemids' => $druleids,
+ 'preservekeys' => true
+ ]);
+ }
+ }
+ }
+
+ // interfaces
+ $interfaces = API::HostInterface()->get([
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'hostids' => zbx_objectValues($dbHosts, 'hostid'),
+ 'nopermissions' => true,
+ 'preservekeys' => true
+ ]);
+
+ if ($update) {
+ $updateDiscoveredValidator = new CUpdateDiscoveredValidator([
+ 'allowed' => ['itemid', 'status'],
+ 'messageAllowedField' => _('Cannot update "%2$s" for a discovered item "%1$s".')
+ ]);
+ foreach ($items as &$item) {
+ // check permissions
+ if (!array_key_exists($item['itemid'], $dbItems)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS,
+ _('No permissions to referred object or it does not exist!')
+ );
+ }
+
+ $dbItem = $dbItems[$item['itemid']];
+
+ if (array_key_exists('hostid', $item) && bccomp($dbItem['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'hostid', _('cannot be changed'))
+ );
+ }
+
+ $itemName = array_key_exists('name', $item) ? $item['name'] : $dbItem['name'];
+
+ // discovered fields, except status, cannot be updated
+ $updateDiscoveredValidator->setObjectName($itemName);
+ $this->checkPartialValidator($item, $updateDiscoveredValidator, $dbItem);
+
+ $item += [
+ 'hostid' => $dbItem['hostid'],
+ 'type' => $dbItem['type'],
+ 'name' => $dbItem['name'],
+ 'key_' => $dbItem['key_'],
+ 'flags' => $dbItem['flags']
+ ];
+ }
+ unset($item);
+ }
+
+ $item_key_parser = new CItemKey();
+ $ip_range_parser = new CIPRangeParser([
+ 'v6' => ZBX_HAVE_IPV6,
+ 'ranges' => false,
+ 'usermacros' => true,
+ 'macros' => [
+ '{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'
+ ]
+ ]);
+ $update_interval_parser = new CUpdateIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => (get_class($this) === 'CItemPrototype')
+ ]);
+
+ $index = 0;
+ foreach ($items as $inum => &$item) {
+ $item = $this->clearValues($item);
+ $index++;
+
+ $fullItem = $items[$inum];
+
+ if (!check_db_fields($itemDbFields, $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ if ($update) {
+ $type = array_key_exists('type', $item) ? $item['type'] : $dbItems[$item['itemid']]['type'];
+
+ if ($type == ITEM_TYPE_HTTPAGENT) {
+ $this->validateHTTPCheck($fullItem, $dbItems[$item['itemid']]);
+ }
+
+ check_db_fields($dbItems[$item['itemid']], $fullItem);
+
+ $this->checkNoParameters(
+ $item,
+ ['templateid', 'state', 'lastlogsize', 'mtime', 'error'],
+ _('Cannot update "%1$s" for item "%2$s".'),
+ $item['name']
+ );
+
+ // apply rules
+ foreach ($this->fieldRules as $field => $rules) {
+ if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
+ $rules['template'] = 1;
+ }
+
+ if ((0 != $fullItem['templateid'] && isset($rules['template'])) || isset($rules['system'])) {
+ unset($item[$field]);
+
+ // For templated item and fields that should not be modified, use the value from DB.
+ if (array_key_exists($field, $dbItems[$item['itemid']])
+ && array_key_exists($field, $fullItem)) {
+ $fullItem[$field] = $dbItems[$item['itemid']][$field];
+ }
+ }
+ }
+
+ if (!isset($item['key_'])) {
+ $item['key_'] = $fullItem['key_'];
+ }
+ if (!isset($item['hostid'])) {
+ $item['hostid'] = $fullItem['hostid'];
+ }
+
+ // If a templated item is being assigned to an interface with a different type, ignore it.
+ $itemInterfaceType = itemTypeInterface($dbItems[$item['itemid']]['type']);
+
+ if ($itemInterfaceType !== INTERFACE_TYPE_ANY && $itemInterfaceType !== INTERFACE_TYPE_OPT
+ && $fullItem['templateid']
+ && array_key_exists('interfaceid', $item) && array_key_exists($item['interfaceid'], $interfaces)
+ && $interfaces[$item['interfaceid']]['type'] != $itemInterfaceType) {
+
+ unset($item['interfaceid']);
+ }
+ }
+ else {
+ if ($fullItem['type'] == ITEM_TYPE_HTTPAGENT) {
+ $this->validateHTTPCheck($fullItem, []);
+ }
+
+ if (!isset($dbHosts[$item['hostid']])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ check_db_fields($itemDbFields, $fullItem);
+
+ $this->checkNoParameters(
+ $item,
+ ['templateid', 'state'],
+ _('Cannot set "%1$s" for item "%2$s".'),
+ $item['name']
+ );
+
+ if ($this instanceof CItemPrototype && (!array_key_exists($fullItem['ruleid'], $discovery_rules)
+ || $discovery_rules[$fullItem['ruleid']]['hostid'] != $fullItem['hostid'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('No permissions to referred object or it does not exist!')
+ );
+ }
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_CALCULATED) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0, 'length' => DB::getFieldLength('items', 'params')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])]
+ ]];
+
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
+ if ($update) {
+ if ($dbItems[$item['itemid']]['type'] == $fullItem['type']) {
+ $flags = API_NOT_EMPTY;
+ }
+ else {
+ $flags = API_REQUIRED | API_NOT_EMPTY;
+ }
+ }
+ else {
+ $flags = API_REQUIRED | API_NOT_EMPTY;
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'params' => ['type' => API_STRING_UTF8, 'flags' => $flags, 'length' => DB::getFieldLength('items', 'params')],
+ 'timeout' => [
+ 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
+ ? $flags | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
+ : $flags | API_ALLOW_USER_MACRO,
+ 'in' => '1:'.SEC_PER_MIN
+ ],
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]]
+ ]];
+
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ $host = $dbHosts[$fullItem['hostid']];
+
+ // Validate update interval.
+ if (!in_array($fullItem['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])
+ && ($fullItem['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($fullItem['key_'], 'mqtt.get', 8) !== 0)
+ && !validateDelay($update_interval_parser, 'delay', $fullItem['delay'], $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ // For non-numeric types, whichever value was entered in trends field, is overwritten to zero.
+ if ($fullItem['value_type'] == ITEM_VALUE_TYPE_STR || $fullItem['value_type'] == ITEM_VALUE_TYPE_LOG
+ || $fullItem['value_type'] == ITEM_VALUE_TYPE_TEXT) {
+ $item['trends'] = '0';
+ }
+
+ // Check if the item requires an interface.
+ if ($host['status'] == HOST_STATUS_TEMPLATE) {
+ unset($item['interfaceid']);
+ }
+ else {
+ $item_interface_type = itemTypeInterface($fullItem['type']);
+
+ if ($item_interface_type !== false) {
+ if (!array_key_exists('interfaceid', $fullItem) || !$fullItem['interfaceid']) {
+ if ($item_interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No interface found.'));
+ }
+ }
+ elseif (!array_key_exists($fullItem['interfaceid'], $interfaces)
+ || bccomp($interfaces[$fullItem['interfaceid']]['hostid'], $fullItem['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses host interface from non-parent host.'));
+ }
+ elseif ($item_interface_type !== INTERFACE_TYPE_ANY && $item_interface_type !== INTERFACE_TYPE_OPT
+ && $interfaces[$fullItem['interfaceid']]['type'] != $item_interface_type) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses incorrect interface type.'));
+ }
+ }
+ // No interface required, just set it to zero.
+ else {
+ $item['interfaceid'] = 0;
+ }
+ }
+
+ // item key
+ if ($fullItem['type'] == ITEM_TYPE_DB_MONITOR) {
+ if (!isset($fullItem['flags']) || $fullItem['flags'] != ZBX_FLAG_DISCOVERY_RULE) {
+ if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Check the key, please. Default example was passed.')
+ );
+ }
+ }
+ elseif ($fullItem['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
+ if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR_DISCOVERY) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Check the key, please. Default example was passed.')
+ );
+ }
+ }
+ }
+ elseif (($fullItem['type'] == ITEM_TYPE_SSH && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_SSH) == 0)
+ || ($fullItem['type'] == ITEM_TYPE_TELNET && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_TELNET) == 0)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Check the key, please. Default example was passed.'));
+ }
+
+ // key
+ if ($item_key_parser->parse($fullItem['key_']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _params($this->getErrorMsg(self::ERROR_INVALID_KEY), [
+ $fullItem['key_'], $fullItem['name'], $host['name'], $item_key_parser->getError()
+ ])
+ );
+ }
+
+ if (($fullItem['type'] == ITEM_TYPE_TRAPPER || $fullItem['type'] == ITEM_TYPE_HTTPAGENT)
+ && array_key_exists('trapper_hosts', $fullItem) && $fullItem['trapper_hosts'] !== ''
+ && !$ip_range_parser->parse($fullItem['trapper_hosts'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', $ip_range_parser->getError())
+ );
+ }
+
+ // jmx
+ if ($fullItem['type'] == ITEM_TYPE_JMX) {
+ if (!array_key_exists('jmx_endpoint', $fullItem) && !$update) {
+ $item['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
+ }
+ if (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('cannot be empty'))
+ );
+ }
+
+ if (($fullItem['username'] === '') !== ($fullItem['password'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'username',
+ _('both username and password should be either present or empty'))
+ );
+ }
+ }
+ else {
+ if (array_key_exists('jmx_endpoint', $item) && $item['jmx_endpoint'] !== '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('should be empty'))
+ );
+ }
+ elseif (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] !== '') {
+ $item['jmx_endpoint'] = '';
+ }
+ }
+
+ // Dependent item.
+ if ($fullItem['type'] == ITEM_TYPE_DEPENDENT) {
+ if ($update) {
+ if (array_key_exists('master_itemid', $item) && !$item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ if ($dbItems[$fullItem['itemid']]['type'] != ITEM_TYPE_DEPENDENT
+ && !array_key_exists('master_itemid', $item)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ }
+ elseif (!array_key_exists('master_itemid', $item) || !$item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ if (array_key_exists('master_itemid', $item) && !is_int($item['master_itemid'])
+ && !(is_string($item['master_itemid']) && ctype_digit($item['master_itemid']))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.',
+ $item['master_itemid'], 'master_itemid'
+ ));
+ }
+ }
+ else {
+ if (array_key_exists('master_itemid', $item) && $item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('should be empty')
+ ));
+ }
+ $item['master_itemid'] = 0;
+ }
+
+ // ssh, telnet
+ if ($fullItem['type'] == ITEM_TYPE_SSH || $fullItem['type'] == ITEM_TYPE_TELNET) {
+ if ($fullItem['username'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No authentication user name specified.'));
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_SSH && $fullItem['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) {
+ if ($fullItem['publickey'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No public key file specified.'));
+ }
+ if ($fullItem['privatekey'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No private key file specified.'));
+ }
+ }
+ }
+
+ // Prevent IPMI sensor field being empty if item key is not "ipmi.get".
+ if ($fullItem['type'] == ITEM_TYPE_IPMI && $fullItem['key_'] !== 'ipmi.get'
+ && (!array_key_exists('ipmi_sensor', $fullItem) || $fullItem['ipmi_sensor'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'ipmi_sensor', _('cannot be empty')
+ ));
+ }
+
+ // snmp trap
+ if ($fullItem['type'] == ITEM_TYPE_SNMPTRAP
+ && $fullItem['key_'] !== 'snmptrap.fallback' && $item_key_parser->getKey() !== 'snmptrap') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('SNMP trap key is invalid.'));
+ }
+
+ // snmp oid
+ if ($fullItem['type'] == ITEM_TYPE_SNMP
+ && (!array_key_exists('snmp_oid', $fullItem) || $fullItem['snmp_oid'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No SNMP OID specified.'));
+ }
+
+ $this->checkSpecificFields($fullItem, $update ? 'update' : 'create');
+
+ $this->validateItemPreprocessing($fullItem);
+ $this->validateTags($item, '/'.$index);
+ }
+ unset($item);
+
+ $this->validateValueMaps($items);
+
+ $this->checkAndAddUuid($items, $dbHosts, $update);
+ $this->checkExistingItems($items);
+ }
+
+ /**
+ * Check that only items on templates have UUID. Add UUID to all host prototypes on templates,
+ * if it doesn't exist.
+ *
+ * @param array $items_to_create
+ * @param array $db_hosts
+ * @param bool $is_update
+ *
+ * @throws APIException
+ */
+ protected function checkAndAddUuid(array &$items_to_create, array $db_hosts, bool $is_update): void {
+ if ($is_update) {
+ foreach ($items_to_create as $index => &$item) {
+ if (array_key_exists('uuid', $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1),
+ _s('unexpected parameter "%1$s"', 'uuid')
+ )
+ );
+ }
+ }
+
+ return;
+ }
+
+ foreach ($items_to_create as $index => &$item) {
+ if ($db_hosts[$item['hostid']]['status'] != HOST_STATUS_TEMPLATE && array_key_exists('uuid', $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1), _s('unexpected parameter "%1$s"', 'uuid'))
+ );
+ }
+
+ if ($db_hosts[$item['hostid']]['status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
+ $item['uuid'] = generateUuidV4();
+ }
+ }
+ unset($item);
+
+ $db_uuid = DB::select('items', [
+ 'output' => ['uuid'],
+ 'filter' => ['uuid' => array_column($items_to_create, 'uuid')],
+ 'limit' => 1
+ ]);
+
+ if ($db_uuid) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Entry with UUID "%1$s" already exists.', $db_uuid[0]['uuid'])
+ );
+ }
+ }
+
+ /**
+ * Validates tags.
+ *
+ * @param array $item
+ * @param array $item['tags']
+ * @param string $item['tags'][]['tag']
+ * @param string $item['tags'][]['value']
+ *
+ * @throws APIException if the input is invalid.
+ */
+ protected function validateTags(array $item, string $path = '/') {
+ if (!array_key_exists('tags', $item)) {
+ return;
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'tags' => ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
+ 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
+ ]]
+ ]];
+
+ $item_tags = ['tags' => $item['tags']];
+ if (!CApiInputValidator::validate($api_input_rules, $item_tags, $path, $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ /**
+ * Check item specific fields. Each API like Item, Itemprototype and Discovery rule may inherit different fields
+ * to validate.
+ *
+ * @param array $item An array of single item data.
+ * @param string $method A string of "create" or "update" method.
+ *
+ * @return bool
+ */
+ abstract protected function checkSpecificFields(array $item, $method);
+
+ protected function clearValues(array $item) {
+ if (isset($item['port']) && $item['port'] != '') {
+ $item['port'] = ltrim($item['port'], '0');
+ if ($item['port'] == '') {
+ $item['port'] = 0;
+ }
+ }
+
+ if (array_key_exists('type', $item) &&
+ ($item['type'] == ITEM_TYPE_DEPENDENT || $item['type'] == ITEM_TYPE_TRAPPER
+ || ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && array_key_exists('key_', $item)
+ && strncmp($item['key_'], 'mqtt.get', 8) === 0))) {
+ $item['delay'] = 0;
+ }
+
+ return $item;
+ }
+
+ protected function errorInheritFlags($flag, $key, $host) {
+ switch ($flag) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_RULE:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as a discovery rule.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item prototype.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item created from item prototype.', $key, $host));
+ break;
+ default:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as unknown item element.', $key, $host));
+ }
+ }
+
+ /**
+ * Return first main interface matched from list of preferred types, or NULL.
+ *
+ * @param array $interfaces An array of interfaces to choose from.
+ *
+ * @return ?array
+ */
+ public static function findInterfaceByPriority(array $interfaces): ?array {
+ $interface_by_type = [];
+
+ foreach ($interfaces as $interface) {
+ if ($interface['main'] == INTERFACE_PRIMARY) {
+ $interface_by_type[$interface['type']] = $interface;
+ }
+ }
+
+ foreach (self::INTERFACE_TYPES_BY_PRIORITY as $interface_type) {
+ if (array_key_exists($interface_type, $interface_by_type)) {
+ return $interface_by_type[$interface_type];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the interface that best matches the given item.
+ *
+ * @param array $item_type An item type
+ * @param array $interfaces An array of interfaces to choose from
+ *
+ * @return array|boolean The best matching interface;
+ * an empty array of no matching interface was found;
+ * false, if the item does not need an interface
+ */
+ public static function findInterfaceForItem($item_type, array $interfaces) {
+ $type = itemTypeInterface($item_type);
+
+ if ($type == INTERFACE_TYPE_OPT) {
+ return false;
+ }
+ elseif ($type == INTERFACE_TYPE_ANY) {
+ return self::findInterfaceByPriority($interfaces);
+ }
+ // the item uses a specific type of interface
+ elseif ($type !== false) {
+ $interface_by_type = [];
+
+ foreach ($interfaces as $interface) {
+ if ($interface['main'] == INTERFACE_PRIMARY) {
+ $interface_by_type[$interface['type']] = $interface;
+ }
+ }
+
+ return array_key_exists($type, $interface_by_type) ? $interface_by_type[$type] : [];
+ }
+ // the item does not need an interface
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the children of the item on the given hosts and propagates the inheritance to the child hosts.
+ *
+ * @param array $tpl_items An array of items to inherit.
+ * @param array|null $hostids An array of hosts to inherit to; if set to null, the items will be inherited to all
+ * linked hosts or templates.
+ */
+ protected function inherit(array $tpl_items, array $hostids = null) {
+ $tpl_items = zbx_toHash($tpl_items, 'itemid');
+
+ // Inherit starting from common items and finishing up dependent.
+ while ($tpl_items) {
+ $_tpl_items = [];
+
+ foreach ($tpl_items as $tpl_item) {
+ if ($tpl_item['type'] != ITEM_TYPE_DEPENDENT
+ || !array_key_exists($tpl_item['master_itemid'], $tpl_items)) {
+ $_tpl_items[$tpl_item['itemid']] = $tpl_item;
+ }
+ }
+
+ foreach ($_tpl_items as $itemid => $_tpl_item) {
+ unset($tpl_items[$itemid]);
+ }
+
+ $this->_inherit($_tpl_items, $hostids);
+ }
+ }
+
+ /**
+ * Auxiliary method for item inheritance. See full description in inherit() method.
+ */
+ private function _inherit(array $tpl_items, array $hostids = null) {
+ // Prepare the child items.
+ $new_items = $this->prepareInheritedItems($tpl_items, $hostids);
+ if (!$new_items) {
+ return;
+ }
+
+ $ins_items = [];
+ $upd_items = [];
+
+ foreach ($new_items as $new_item) {
+ if (array_key_exists('itemid', $new_item)) {
+ if ($this instanceof CItemPrototype) {
+ unset($new_item['ruleid']);
+ }
+ $upd_items[$new_item['itemid']] = $new_item;
+ }
+ else {
+ $ins_items[] = $new_item;
+ }
+ }
+
+ $this->validateDependentItems($new_items);
+
+ // Save the new items.
+ if ($ins_items) {
+ if ($this instanceof CItem) {
+ static::validateInventoryLinks($ins_items, false);
+ }
+
+ $this->createReal($ins_items);
+ }
+
+ if ($upd_items) {
+ if ($this instanceof CItem) {
+ static::validateInventoryLinks($upd_items, true);
+ }
+
+ $this->updateReal($upd_items);
+ }
+
+ $new_items = array_merge($upd_items, $ins_items);
+
+ // Inheriting items from the templates.
+ $db_items = DBselect(
+ 'SELECT i.itemid'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionInt('i.itemid', zbx_objectValues($new_items, 'itemid')).
+ ' AND '.dbConditionInt('h.status', [HOST_STATUS_TEMPLATE])
+ );
+
+ $tpl_itemids = [];
+ while ($db_item = DBfetch($db_items)) {
+ $tpl_itemids[$db_item['itemid']] = true;
+ }
+
+ foreach ($new_items as $index => $new_item) {
+ if (!array_key_exists($new_item['itemid'], $tpl_itemids)) {
+ unset($new_items[$index]);
+ }
+ }
+
+ $this->inherit($new_items);
+ }
+
+ /**
+ * Prepares and returns an array of child items, inherited from items $tpl_items on the given hosts.
+ *
+ * @param array $tpl_items
+ * @param string $tpl_items[<itemid>]['itemid']
+ * @param string $tpl_items[<itemid>]['hostid']
+ * @param string $tpl_items[<itemid>]['key_']
+ * @param int $tpl_items[<itemid>]['type']
+ * @param array $tpl_items[<itemid>]['preprocessing'] (optional)
+ * @param int $tpl_items[<itemid>]['preprocessing'][]['type']
+ * @param string $tpl_items[<itemid>]['preprocessing'][]['params']
+ * @param int $tpl_items[<itemid>]['flags']
+ * @param string $tpl_items[<itemid>]['master_itemid'] (optional)
+ * @param mixed $tpl_items[<itemid>][<field_name>] (optional)
+ * @param array|null $hostids
+ *
+ * @return array an array of unsaved child items
+ */
+ private function prepareInheritedItems(array $tpl_items, array $hostids = null) {
+ $itemids_by_templateid = [];
+ foreach ($tpl_items as $tpl_item) {
+ $itemids_by_templateid[$tpl_item['hostid']][] = $tpl_item['itemid'];
+ }
+
+ // Fetch all child hosts.
+ $chd_hosts = API::Host()->get([
+ 'output' => ['hostid', 'host', 'status'],
+ 'selectParentTemplates' => ['templateid'],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type'],
+ 'templateids' => array_keys($itemids_by_templateid),
+ 'hostids' => $hostids,
+ 'preservekeys' => true,
+ 'nopermissions' => true,
+ 'templated_hosts' => true
+ ]);
+ if (!$chd_hosts) {
+ return [];
+ }
+
+ $chd_items_tpl = [];
+ $chd_items_key = [];
+
+ // Preparing list of items by item templateid.
+ $sql = 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.templateid', zbx_objectValues($tpl_items, 'itemid'));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
+ }
+ $db_items = DBselect($sql);
+
+ while ($db_item = DBfetch($db_items)) {
+ $hostid = $db_item['hostid'];
+ unset($db_item['hostid']);
+
+ $chd_items_tpl[$hostid][$db_item['templateid']] = $db_item;
+ }
+
+ $hostids_by_key = [];
+
+ // Preparing list of items by item key.
+ foreach ($chd_hosts as $chd_host) {
+ $tpl_itemids = [];
+
+ foreach ($chd_host['parentTemplates'] as $parent_template) {
+ if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
+ $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ }
+ }
+
+ foreach ($tpl_itemids as $tpl_itemid) {
+ if (!array_key_exists($chd_host['hostid'], $chd_items_tpl)
+ || !array_key_exists($tpl_itemid, $chd_items_tpl[$chd_host['hostid']])) {
+ $hostids_by_key[$tpl_items[$tpl_itemid]['key_']][] = $chd_host['hostid'];
+ }
+ }
+ }
+
+ foreach ($hostids_by_key as $key_ => $key_hostids) {
+ $sql_select = ($this instanceof CItemPrototype) ? ',id.parent_itemid AS ruleid' : '';
+ // "LEFT JOIN" is needed to check flags on inherited and existing item, item prototype or lld rule.
+ // For example, when linking an item prototype with same key as in an item on target host or template.
+ $sql_join = ($this instanceof CItemPrototype) ? ' LEFT JOIN item_discovery id ON i.itemid=id.itemid' : '';
+ $db_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.$sql_select.
+ ' FROM items i'.$sql_join.
+ ' WHERE '.dbConditionInt('i.hostid', $key_hostids).
+ ' AND '.dbConditionString('i.key_', [$key_])
+ );
+
+ while ($db_item = DBfetch($db_items)) {
+ $hostid = $db_item['hostid'];
+ unset($db_item['hostid']);
+
+ $chd_items_key[$hostid][$db_item['key_']] = $db_item;
+ }
+ }
+
+ // List of the discovery rules.
+ if ($this instanceof CItemPrototype) {
+ // List of itemids without 'ruleid' property.
+ $tpl_itemids = [];
+ $tpl_ruleids = [];
+ foreach ($tpl_items as $tpl_item) {
+ if (!array_key_exists('ruleid', $tpl_item)) {
+ $tpl_itemids[] = $tpl_item['itemid'];
+ }
+ else {
+ $tpl_ruleids[$tpl_item['ruleid']] = true;
+ }
+ }
+
+ if ($tpl_itemids) {
+ $db_rules = DBselect(
+ 'SELECT id.parent_itemid,id.itemid'.
+ ' FROM item_discovery id'.
+ ' WHERE '.dbConditionInt('id.itemid', $tpl_itemids)
+ );
+
+ while ($db_rule = DBfetch($db_rules)) {
+ $tpl_items[$db_rule['itemid']]['ruleid'] = $db_rule['parent_itemid'];
+ $tpl_ruleids[$db_rule['parent_itemid']] = true;
+ }
+ }
+
+ $sql = 'SELECT i.hostid,i.templateid,i.itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.templateid', array_keys($tpl_ruleids));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
+ }
+ $db_rules = DBselect($sql);
+
+ // List of child lld ruleids by child hostid and parent lld ruleid.
+ $chd_ruleids = [];
+ while ($db_rule = DBfetch($db_rules)) {
+ $chd_ruleids[$db_rule['hostid']][$db_rule['templateid']] = $db_rule['itemid'];
+ }
+ }
+
+ $new_items = [];
+ // List of the updated item keys by hostid.
+ $upd_hostids_by_key = [];
+
+ foreach ($chd_hosts as $chd_host) {
+ $tpl_itemids = [];
+
+ foreach ($chd_host['parentTemplates'] as $parent_template) {
+ if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
+ $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ }
+ }
+
+ foreach ($tpl_itemids as $tpl_itemid) {
+ $tpl_item = $tpl_items[$tpl_itemid];
+
+ $chd_item = null;
+
+ // Update by templateid.
+ if (array_key_exists($chd_host['hostid'], $chd_items_tpl)
+ && array_key_exists($tpl_item['itemid'], $chd_items_tpl[$chd_host['hostid']])) {
+ $chd_item = $chd_items_tpl[$chd_host['hostid']][$tpl_item['itemid']];
+
+ if ($tpl_item['key_'] !== $chd_item['key_']) {
+ $upd_hostids_by_key[$tpl_item['key_']][] = $chd_host['hostid'];
+ }
+ }
+ // Update by key.
+ elseif (array_key_exists($chd_host['hostid'], $chd_items_key)
+ && array_key_exists($tpl_item['key_'], $chd_items_key[$chd_host['hostid']])) {
+ $chd_item = $chd_items_key[$chd_host['hostid']][$tpl_item['key_']];
+
+ // Check if an item of a different type with the same key exists.
+ if ($tpl_item['flags'] != $chd_item['flags']) {
+ $this->errorInheritFlags($chd_item['flags'], $chd_item['key_'], $chd_host['host']);
+ }
+
+ // Check if item already linked to another template.
+ if ($chd_item['templateid'] != 0 && bccomp($chd_item['templateid'], $tpl_item['itemid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params(
+ $this->getErrorMsg(self::ERROR_EXISTS_TEMPLATE), [$tpl_item['key_'], $chd_host['host']]
+ ));
+ }
+
+ if ($this instanceof CItemPrototype) {
+ $chd_ruleid = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
+ if (bccomp($chd_item['ruleid'], $chd_ruleid) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item prototype "%1$s" already exists on "%2$s", linked to another rule.',
+ $chd_item['key_'], $chd_host['host']
+ )
+ );
+ }
+ }
+ }
+
+ // copying item
+ $new_item = $tpl_item;
+ $new_item['uuid'] = '';
+
+ if ($chd_item !== null) {
+ $new_item['itemid'] = $chd_item['itemid'];
+
+ if ($new_item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $new_item['interfaceid'] = null;
+ }
+ }
+ else {
+ unset($new_item['itemid']);
+ if ($this instanceof CItemPrototype) {
+ $new_item['ruleid'] = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
+ }
+ }
+ $new_item['hostid'] = $chd_host['hostid'];
+ $new_item['templateid'] = $tpl_item['itemid'];
+
+ if ($chd_host['status'] != HOST_STATUS_TEMPLATE) {
+ if ($chd_item === null || $new_item['type'] != $chd_item['type']) {
+ $interface = self::findInterfaceForItem($new_item['type'], $chd_host['interfaces']);
+
+ if ($interface) {
+ $new_item['interfaceid'] = $interface['interfaceid'];
+ }
+ elseif ($interface !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params(
+ $this->getErrorMsg(self::ERROR_NO_INTERFACE), [$chd_host['host'], $new_item['key_']]
+ ));
+ }
+ }
+
+ if ($this instanceof CItem || $this instanceof CDiscoveryRule) {
+ if (!array_key_exists('itemid', $new_item)) {
+ $new_item['rtdata'] = true;
+ }
+ }
+ }
+
+ if (array_key_exists('preprocessing', $new_item)) {
+ foreach ($new_item['preprocessing'] as $preprocessing) {
+ if ($chd_item) {
+ $preprocessing['itemid'] = $chd_item['itemid'];
+ }
+ else {
+ unset($preprocessing['itemid']);
+ }
+ }
+ }
+
+ $new_items[] = $new_item;
+ }
+ }
+
+ // Check if item with a new key already exists on the child host.
+ if ($upd_hostids_by_key) {
+ $sql_where = [];
+ foreach ($upd_hostids_by_key as $key => $hostids) {
+ $sql_where[] = dbConditionInt('i.hostid', $hostids).' AND i.key_='.zbx_dbstr($key);
+ }
+
+ $sql = 'SELECT i.hostid,i.key_'.
+ ' FROM items i'.
+ ' WHERE ('.implode(') OR (', $sql_where).')';
+ $db_items = DBselect($sql, 1);
+
+ if ($db_item = DBfetch($db_items)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params($this->getErrorMsg(self::ERROR_EXISTS),
+ [$db_item['key_'], $chd_hosts[$db_item['hostid']]['host']]
+ ));
+ }
+ }
+
+ return $this->prepareDependentItems($tpl_items, $new_items, $hostids);
+ }
+
+ /**
+ * Update relations for inherited dependent items to master items.
+ *
+ * @param array $tpl_items
+ * @param int $tpl_items[<itemid>]['type']
+ * @param string $tpl_items[<itemid>]['master_itemid']
+ * @param array $new_items
+ * @param string $new_items[<itemid>]['hostid']
+ * @param int $new_items[<itemid>]['type']
+ * @param string $new_items[<itemid>]['templateid']
+ * @param array|null $hostids
+ *
+ * @return array an array of synchronized inherited items.
+ */
+ private function prepareDependentItems(array $tpl_items, array $new_items, array $hostids = null) {
+ $tpl_master_itemids = [];
+
+ foreach ($tpl_items as $tpl_item) {
+ if ($tpl_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $tpl_master_itemids[$tpl_item['master_itemid']] = true;
+ }
+ }
+
+ if ($tpl_master_itemids) {
+ $sql = 'SELECT i.itemid,i.hostid,i.templateid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.templateid', array_keys($tpl_master_itemids));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionId('i.hostid', $hostids);
+ }
+ $db_items = DBselect($sql);
+
+ $master_links = [];
+
+ while ($db_item = DBfetch($db_items)) {
+ $master_links[$db_item['templateid']][$db_item['hostid']] = $db_item['itemid'];
+ }
+
+ foreach ($new_items as &$new_item) {
+ if ($new_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $tpl_item = $tpl_items[$new_item['templateid']];
+
+ if (array_key_exists('master_itemid', $tpl_item)) {
+ $new_item['master_itemid'] = $master_links[$tpl_item['master_itemid']][$new_item['hostid']];
+ }
+ }
+ }
+ unset($new_item);
+ }
+
+ return $new_items;
+ }
+
+ /**
+ * Validate item pre-processing.
+ *
+ * @param array $item An array of single item data.
+ * @param array $item['preprocessing'] An array of item pre-processing data.
+ * @param string $item['preprocessing'][]['type'] The preprocessing option type. Possible values:
+ * 1 - ZBX_PREPROC_MULTIPLIER;
+ * 2 - ZBX_PREPROC_RTRIM;
+ * 3 - ZBX_PREPROC_LTRIM;
+ * 4 - ZBX_PREPROC_TRIM;
+ * 5 - ZBX_PREPROC_REGSUB;
+ * 6 - ZBX_PREPROC_BOOL2DEC;
+ * 7 - ZBX_PREPROC_OCT2DEC;
+ * 8 - ZBX_PREPROC_HEX2DEC;
+ * 9 - ZBX_PREPROC_DELTA_VALUE;
+ * 10 - ZBX_PREPROC_DELTA_SPEED;
+ * 11 - ZBX_PREPROC_XPATH;
+ * 12 - ZBX_PREPROC_JSONPATH;
+ * 13 - ZBX_PREPROC_VALIDATE_RANGE;
+ * 14 - ZBX_PREPROC_VALIDATE_REGEX;
+ * 15 - ZBX_PREPROC_VALIDATE_NOT_REGEX;
+ * 16 - ZBX_PREPROC_ERROR_FIELD_JSON;
+ * 17 - ZBX_PREPROC_ERROR_FIELD_XML;
+ * 18 - ZBX_PREPROC_ERROR_FIELD_REGEX;
+ * 19 - ZBX_PREPROC_THROTTLE_VALUE;
+ * 20 - ZBX_PREPROC_THROTTLE_TIMED_VALUE;
+ * 21 - ZBX_PREPROC_SCRIPT;
+ * 22 - ZBX_PREPROC_PROMETHEUS_PATTERN;
+ * 23 - ZBX_PREPROC_PROMETHEUS_TO_JSON;
+ * 24 - ZBX_PREPROC_CSV_TO_JSON;
+ * 25 - ZBX_PREPROC_STR_REPLACE;
+ * 26 - ZBX_PREPROC_VALIDATE_NOT_SUPPORTED;
+ * @param string $item['preprocessing'][]['params'] Additional parameters used by preprocessing
+ * option. Multiple parameters are separated by LF
+ * (\n) character.
+ * @param string $item['preprocessing'][]['error_handler'] Action type used in case of preprocessing step
+ * failure. Possible values:
+ * 0 - ZBX_PREPROC_FAIL_DEFAULT;
+ * 1 - ZBX_PREPROC_FAIL_DISCARD_VALUE;
+ * 2 - ZBX_PREPROC_FAIL_SET_VALUE;
+ * 3 - ZBX_PREPROC_FAIL_SET_ERROR.
+ * @param string $item['preprocessing'][]['error_handler_params'] Error handler parameters.
+ */
+ protected function validateItemPreprocessing(array $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ if (!is_array($item['preprocessing'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ $type_validator = new CLimitedSetValidator(['values' => static::SUPPORTED_PREPROCESSING_TYPES]);
+
+ $error_handler_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE,
+ ZBX_PREPROC_FAIL_SET_ERROR
+ ]
+ ]);
+
+ $unsupported_error_handler_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR]
+ ]);
+
+ $prometheus_pattern_parser = new CPrometheusPatternParser(['usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+ $prometheus_output_parser = new CPrometheusOutputParser(['usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+
+ $required_fields = ['type', 'params', 'error_handler', 'error_handler_params'];
+ $delta = false;
+ $throttling = false;
+ $prometheus = false;
+
+ foreach ($item['preprocessing'] as $preprocessing) {
+ $missing_keys = array_diff($required_fields, array_keys($preprocessing));
+
+ if ($missing_keys) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item pre-processing is missing parameters: %1$s', implode(', ', $missing_keys))
+ );
+ }
+
+ if (is_array($preprocessing['type'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['type'] === '' || $preprocessing['type'] === null
+ || $preprocessing['type'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'type', _('cannot be empty'))
+ );
+ }
+
+ if (!$type_validator->validate($preprocessing['type'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'type',
+ _s('unexpected value "%1$s"', $preprocessing['type'])
+ )
+ );
+ }
+
+ $preprocessing['params'] = str_replace("\r\n", "\n", $preprocessing['params']);
+
+ switch ($preprocessing['type']) {
+ case ZBX_PREPROC_MULTIPLIER:
+ // Check if custom multiplier is a valid number.
+ $params = $preprocessing['params'];
+
+ if (is_array($params)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($params === '' || $params === null || $params === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ if (is_numeric($params)) {
+ break;
+ }
+
+ $types = ['usermacros' => true];
+
+ if ($this instanceof CItemPrototype) {
+ $types['lldmacros'] = true;
+ }
+
+ if (!(new CMacrosResolverGeneral)->getMacroPositions($params, $types)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ case ZBX_PREPROC_XPATH:
+ case ZBX_PREPROC_JSONPATH:
+ case ZBX_PREPROC_VALIDATE_REGEX:
+ case ZBX_PREPROC_VALIDATE_NOT_REGEX:
+ case ZBX_PREPROC_ERROR_FIELD_JSON:
+ case ZBX_PREPROC_ERROR_FIELD_XML:
+ case ZBX_PREPROC_SCRIPT:
+ // Check 'params' if not empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_REGSUB:
+ case ZBX_PREPROC_ERROR_FIELD_REGEX:
+ case ZBX_PREPROC_STR_REPLACE:
+ // Check if 'params' are not empty and if second parameter contains (after \n) is not empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('first parameter is expected')
+ ));
+ }
+
+ if (($preprocessing['type'] == ZBX_PREPROC_REGSUB
+ || $preprocessing['type'] == ZBX_PREPROC_ERROR_FIELD_REGEX)
+ && (!array_key_exists(1, $params) || $params[1] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_RANGE:
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (trim($preprocessing['params']) === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] !== '' && !is_numeric($params[0])
+ && (new CUserMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS
+ && (!($this instanceof CItemPrototype)
+ || ((new CLLDMacroFunctionParser())->parse($params[0]) != CParser::PARSE_SUCCESS
+ && (new CLLDMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+
+ if ($params[1] !== '' && !is_numeric($params[1])
+ && (new CUserMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS
+ && (!($this instanceof CItemPrototype)
+ || ((new CLLDMacroFunctionParser())->parse($params[1]) != CParser::PARSE_SUCCESS
+ && (new CLLDMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+
+ if (is_numeric($params[0]) && is_numeric($params[1]) && $params[0] > $params[1]) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Incorrect value for field "%1$s": %2$s.',
+ 'params',
+ _s('"%1$s" value must be less than or equal to "%2$s" value', _('min'), _('max'))
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_BOOL2DEC:
+ case ZBX_PREPROC_OCT2DEC:
+ case ZBX_PREPROC_HEX2DEC:
+ case ZBX_PREPROC_THROTTLE_VALUE:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_THROTTLE_VALUE) {
+ if ($throttling) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
+ }
+ else {
+ $throttling = true;
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_DELTA_VALUE:
+ case ZBX_PREPROC_DELTA_SPEED:
+ case ZBX_PREPROC_XML_TO_JSON:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_DELTA_VALUE
+ || $preprocessing['type'] == ZBX_PREPROC_DELTA_SPEED) {
+ // Check if one of the deltas (Delta per second or Delta value) already exists.
+ if ($delta) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one change step is allowed.'));
+ }
+ else {
+ $delta = true;
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ $api_input_rules = [
+ 'type' => API_TIME_UNIT,
+ 'flags' => ($this instanceof CItem)
+ ? API_NOT_EMPTY | API_ALLOW_USER_MACRO
+ : API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO,
+ 'in' => '1:'.ZBX_MAX_TIMESHIFT
+ ];
+
+ if (!CApiInputValidator::validate($api_input_rules, $preprocessing['params'], 'params',
+ $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ if ($throttling) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
+ }
+ else {
+ $throttling = true;
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ case ZBX_PREPROC_PROMETHEUS_TO_JSON:
+ if ($prometheus) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one Prometheus step is allowed.'));
+ }
+
+ $prometheus = true;
+
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_PROMETHEUS_PATTERN) {
+ if ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('first parameter is expected')
+ ));
+ }
+ elseif (!array_key_exists(1, $params)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ elseif (!array_key_exists(2, $params)
+ && ($params[1] === ZBX_PREPROC_PROMETHEUS_LABEL
+ || $params[1] === ZBX_PREPROC_PROMETHEUS_FUNCTION)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('third parameter is expected')
+ ));
+ }
+
+ if ($prometheus_pattern_parser->parse($params[0]) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid Prometheus pattern')
+ ));
+ }
+
+ if (!in_array($params[1], [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL,
+ ZBX_PREPROC_PROMETHEUS_FUNCTION])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid aggregation method')
+ ));
+ }
+
+ switch ($params[1]) {
+ case ZBX_PREPROC_PROMETHEUS_VALUE:
+ if (array_key_exists(2, $params) && $params[2] !== '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('invalid Prometheus output')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_LABEL:
+ if ($prometheus_output_parser->parse($params[2]) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('invalid Prometheus output')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_FUNCTION:
+ if (!in_array($params[2], [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN,
+ ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG,
+ ZBX_PREPROC_PROMETHEUS_COUNT])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('unsupported Prometheus function')
+ )
+ );
+ }
+ break;
+ }
+ }
+ // Prometheus to JSON can be empty and has only one parameter.
+ elseif ($preprocessing['params'] !== '') {
+ if ($prometheus_pattern_parser->parse($preprocessing['params']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid Prometheus pattern')
+ ));
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_CSV_TO_JSON:
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ $params_cnt = count($params);
+ if ($params_cnt > 3) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($params_cnt == 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ elseif ($params_cnt == 2) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('third parameter is expected')
+ ));
+ }
+ else {
+ // Correct amount of parameters, but check if they are valid.
+
+ if (mb_strlen($params[0]) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('value of first parameter is too long')
+ ));
+ }
+
+ if (mb_strlen($params[1]) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('value of second parameter is too long')
+ ));
+ }
+
+ $with_header_row_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER]
+ ]);
+
+ if (!$with_header_row_validator->validate($params[2])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _s('value of third parameter must be one of %1$s',
+ implode(', ', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])
+ )
+ )
+ );
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ $preprocessing_types = array_column($item['preprocessing'], 'type');
+
+ if (count(array_keys($preprocessing_types, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED)) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Only one not supported value check is allowed.')
+ );
+ }
+ break;
+ }
+
+ switch ($preprocessing['type']) {
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ case ZBX_PREPROC_THROTTLE_VALUE:
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ case ZBX_PREPROC_SCRIPT:
+ case ZBX_PREPROC_STR_REPLACE:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler'] != ZBX_PREPROC_FAIL_DEFAULT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (!$unsupported_error_handler_validator->validate($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE
+ && $preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
+ && ($preprocessing['error_handler_params'] === ''
+ || $preprocessing['error_handler_params'] === null
+ || $preprocessing['error_handler_params'] === false)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('cannot be empty')
+ )
+ );
+ }
+ break;
+
+ default:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (!$error_handler_validator->validate($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT
+ || $preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE)
+ && $preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
+ && ($preprocessing['error_handler_params'] === ''
+ || $preprocessing['error_handler_params'] === null
+ || $preprocessing['error_handler_params'] === false)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('cannot be empty')
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Method validates preprocessing steps independently from other item properties.
+ *
+ * @param array $preprocessing_steps An array of item pre-processing step details.
+ * See self::validateItemPreprocessing for details.
+ *
+ * @return bool|string
+ */
+ public function validateItemPreprocessingSteps(array $preprocessing_steps) {
+ try {
+ $this->validateItemPreprocessing(['preprocessing' => $preprocessing_steps]);
+
+ return true;
+ }
+ catch (APIException $error) {
+ return $error->getMessage();
+ }
+ }
+
+ /**
+ * Insert item pre-processing data into DB.
+ *
+ * @param array $items An array of items.
+ * @param string $items[]['itemid']
+ * @param array $items[]['preprocessing'] An array of item pre-processing data.
+ */
+ protected function createItemPreprocessing(array $items) {
+ $item_preproc = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $step = 1;
+
+ foreach ($item['preprocessing'] as $preprocessing) {
+ $item_preproc[] = [
+ 'itemid' => $item['itemid'],
+ 'step' => ($preprocessing['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++,
+ 'type' => $preprocessing['type'],
+ 'params' => $preprocessing['params'],
+ 'error_handler' => $preprocessing['error_handler'],
+ 'error_handler_params' => $preprocessing['error_handler_params']
+ ];
+ }
+ }
+ }
+
+ if ($item_preproc) {
+ DB::insertBatch('item_preproc', $item_preproc);
+ }
+ }
+
+ /**
+ * Update item pre-processing data in DB. Delete old records and create new ones.
+ *
+ * @param array $items
+ * @param string $items[]['itemid']
+ * @param array $items[]['preprocessing']
+ * @param int $items[]['preprocessing'][]['type']
+ * @param string $items[]['preprocessing'][]['params']
+ * @param int $items[]['preprocessing'][]['error_handler']
+ * @param string $items[]['preprocessing'][]['error_handler_params']
+ */
+ protected function updateItemPreprocessing(array $items) {
+ $item_preprocs = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $item_preprocs[$item['itemid']] = [];
+ $step = 1;
+
+ foreach ($item['preprocessing'] as $item_preproc) {
+ $curr_step = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
+ $item_preprocs[$item['itemid']][$curr_step] = [
+ 'type' => $item_preproc['type'],
+ 'params' => $item_preproc['params'],
+ 'error_handler' => $item_preproc['error_handler'],
+ 'error_handler_params' => $item_preproc['error_handler_params']
+ ];
+ }
+ }
+ }
+
+ if (!$item_preprocs) {
+ return;
+ }
+
+ $ins_item_preprocs = [];
+ $upd_item_preprocs = [];
+ $del_item_preprocids = [];
+
+ $options = [
+ 'output' => ['item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'],
+ 'filter' => ['itemid' => array_keys($item_preprocs)]
+ ];
+ $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+
+ while ($db_item_preproc = DBfetch($db_item_preprocs)) {
+ if (array_key_exists($db_item_preproc['step'], $item_preprocs[$db_item_preproc['itemid']])) {
+ $item_preproc = $item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']];
+ $upd_item_preproc = [];
+
+ if ($item_preproc['type'] != $db_item_preproc['type']) {
+ $upd_item_preproc['type'] = $item_preproc['type'];
+ }
+ if ($item_preproc['params'] !== $db_item_preproc['params']) {
+ $upd_item_preproc['params'] = $item_preproc['params'];
+ }
+ if ($item_preproc['error_handler'] != $db_item_preproc['error_handler']) {
+ $upd_item_preproc['error_handler'] = $item_preproc['error_handler'];
+ }
+ if ($item_preproc['error_handler_params'] !== $db_item_preproc['error_handler_params']) {
+ $upd_item_preproc['error_handler_params'] = $item_preproc['error_handler_params'];
+ }
+
+ if ($upd_item_preproc) {
+ $upd_item_preprocs[] = [
+ 'values' => $upd_item_preproc,
+ 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
+ ];
+ }
+ unset($item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']]);
+ }
+ else {
+ $del_item_preprocids[] = $db_item_preproc['item_preprocid'];
+ }
+ }
+
+ foreach ($item_preprocs as $itemid => $preprocs) {
+ foreach ($preprocs as $step => $preproc) {
+ $ins_item_preprocs[] = [
+ 'itemid' => $itemid,
+ 'step' => $step
+ ] + $preproc;
+ }
+ }
+
+ if ($del_item_preprocids) {
+ DB::delete('item_preproc', ['item_preprocid' => $del_item_preprocids]);
+ }
+
+ if ($upd_item_preprocs) {
+ DB::update('item_preproc', $upd_item_preprocs);
+ }
+
+ if ($ins_item_preprocs) {
+ DB::insertBatch('item_preproc', $ins_item_preprocs);
+ }
+ }
+
+ /**
+ * Create item parameters.
+ *
+ * @param array $items Array of items.
+ * @param array $items[]['parameters'] Item parameters.
+ * @param array $items[]['parameters'][]['name'] Parameter name.
+ * @param array $items[]['parameters'][]['value'] Parameter value.
+ * @param array $itemids Array of item IDs that were created before.
+ */
+ protected function createItemParameters(array $items, array $itemids): void {
+ $item_parameters = [];
+
+ foreach ($items as $key => $item) {
+ $items[$key]['itemid'] = $itemids[$key];
+
+ if (!array_key_exists('parameters', $item) || !$item['parameters']) {
+ continue;
+ }
+
+ foreach ($item['parameters'] as $parameter) {
+ $item_parameters[] = [
+ 'itemid' => $items[$key]['itemid'],
+ 'name' => $parameter['name'],
+ 'value' => $parameter['value']
+ ];
+ }
+ }
+
+ if ($item_parameters) {
+ DB::insertBatch('item_parameter', $item_parameters);
+ }
+ }
+
+ /**
+ * Update item parameters.
+ *
+ * @param array $items Array of items.
+ * @param int|string $items[]['itemid'] Item ID.
+ * @param int|string $items[]['type'] Item type.
+ * @param array $items[]['parameters'] Item parameters.
+ * @param array $items[]['parameters'][]['name'] Parameter name.
+ * @param array $items[]['parameters'][]['value'] Parameter value.
+ */
+ protected function updateItemParameters(array $items): void {
+ $db_item_parameters_by_itemid = [];
+
+ foreach ($items as $item) {
+ if ($item['type'] != ITEM_TYPE_SCRIPT || array_key_exists('parameters', $item)) {
+ $db_item_parameters_by_itemid[$item['itemid']] = [];
+ }
+ }
+
+ if (!$db_item_parameters_by_itemid) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
+ 'filter' => ['itemid' => array_keys($db_item_parameters_by_itemid)]
+ ];
+ $result = DBselect(DB::makeSql('item_parameter', $options));
+
+ while ($row = DBfetch($result)) {
+ $db_item_parameters_by_itemid[$row['itemid']][$row['name']] = [
+ 'item_parameterid' => $row['item_parameterid'],
+ 'value' => $row['value']
+ ];
+ }
+
+ $ins_item_parameters = [];
+ $upd_item_parameters = [];
+ $del_item_parameterids = [];
+
+ foreach ($db_item_parameters_by_itemid as $itemid => $db_item_parameters) {
+ $item = $items[$itemid];
+
+ if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item)) {
+ foreach ($item['parameters'] as $parameter) {
+ if (array_key_exists($parameter['name'], $db_item_parameters)) {
+ if ($db_item_parameters[$parameter['name']]['value'] !== $parameter['value']) {
+ $upd_item_parameters[] = [
+ 'values' => ['value' => $parameter['value']],
+ 'where' => [
+ 'item_parameterid' => $db_item_parameters[$parameter['name']]['item_parameterid']
+ ]
+ ];
+ }
+ unset($db_item_parameters[$parameter['name']]);
+ }
+ else {
+ $ins_item_parameters[] = [
+ 'itemid' => $itemid,
+ 'name' => $parameter['name'],
+ 'value' => $parameter['value']
+ ];
+ }
+ }
+ }
+
+ $del_item_parameterids = array_merge($del_item_parameterids,
+ array_column($db_item_parameters, 'item_parameterid')
+ );
+ }
+
+ if ($del_item_parameterids) {
+ DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ }
+
+ if ($upd_item_parameters) {
+ DB::update('item_parameter', $upd_item_parameters);
+ }
+
+ if ($ins_item_parameters) {
+ DB::insertBatch('item_parameter', $ins_item_parameters);
+ }
+ }
+
+ /**
+ * Check if any item from list already exists.
+ * If items have item ids it will check for existing item with different itemid.
+ *
+ * @throw APIException
+ *
+ * @param array $items
+ */
+ protected function checkExistingItems(array $items) {
+ $itemKeysByHostId = [];
+ $itemIds = [];
+ foreach ($items as $item) {
+ if (!isset($itemKeysByHostId[$item['hostid']])) {
+ $itemKeysByHostId[$item['hostid']] = [];
+ }
+ $itemKeysByHostId[$item['hostid']][] = $item['key_'];
+
+ if (isset($item['itemid'])) {
+ $itemIds[] = $item['itemid'];
+ }
+ }
+
+ $sqlWhere = [];
+ foreach ($itemKeysByHostId as $hostId => $keys) {
+ $sqlWhere[] = '(i.hostid='.zbx_dbstr($hostId).' AND '.dbConditionString('i.key_', $keys).')';
+ }
+
+ if ($sqlWhere) {
+ $sql = 'SELECT i.key_,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid AND ('.implode(' OR ', $sqlWhere).')';
+
+ // if we update existing items we need to exclude them from result.
+ if ($itemIds) {
+ $sql .= ' AND '.dbConditionInt('i.itemid', $itemIds, true);
+ }
+ $dbItems = DBselect($sql, 1);
+ while ($dbItem = DBfetch($dbItems)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item with key "%1$s" already exists on "%2$s".', $dbItem['key_'], $dbItem['host']));
+ }
+ }
+ }
+
+ protected function addRelatedObjects(array $options, array $result) {
+ $result = parent::addRelatedObjects($options, $result);
+
+ // adding hosts
+ if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) {
+ $relationMap = $this->createRelationMap($result, 'itemid', 'hostid');
+ $hosts = API::Host()->get([
+ 'hostids' => $relationMap->getRelatedIds(),
+ 'templated_hosts' => true,
+ 'output' => $options['selectHosts'],
+ 'nopermissions' => true,
+ 'preservekeys' => true
+ ]);
+ $result = $relationMap->mapMany($result, $hosts, 'hosts');
+ }
+
+ // adding preprocessing
+ if ($options['selectPreprocessing'] !== null && $options['selectPreprocessing'] != API_OUTPUT_COUNT) {
+ $db_item_preproc = API::getApiService()->select('item_preproc', [
+ 'output' => $this->outputExtend($options['selectPreprocessing'], ['itemid', 'step']),
+ 'filter' => ['itemid' => array_keys($result)]
+ ]);
+
+ CArrayHelper::sort($db_item_preproc, ['step']);
+
+ foreach ($result as &$item) {
+ $item['preprocessing'] = [];
+ }
+ unset($item);
+
+ foreach ($db_item_preproc as $step) {
+ $itemid = $step['itemid'];
+ unset($step['item_preprocid'], $step['itemid'], $step['step']);
+
+ if (array_key_exists($itemid, $result)) {
+ $result[$itemid]['preprocessing'][] = $step;
+ }
+ }
+ }
+
+ // Add value mapping.
+ if (($this instanceof CItemPrototype || $this instanceof CItem) && $options['selectValueMap'] !== null) {
+ if ($options['selectValueMap'] === API_OUTPUT_EXTEND) {
+ $options['selectValueMap'] = ['valuemapid', 'name', 'mappings'];
+ }
+
+ foreach ($result as &$item) {
+ $item['valuemap'] = [];
+ }
+ unset($item);
+
+ $valuemaps = DB::select('valuemap', [
+ 'output' => array_diff($this->outputExtend($options['selectValueMap'], ['valuemapid', 'hostid']),
+ ['mappings']
+ ),
+ 'filter' => ['valuemapid' => array_keys(array_flip(array_column($result, 'valuemapid')))],
+ 'preservekeys' => true
+ ]);
+
+ if ($this->outputIsRequested('mappings', $options['selectValueMap']) && $valuemaps) {
+ $params = [
+ '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']
+ ];
+ }
+ }
+
+ foreach ($result as &$item) {
+ if (array_key_exists('valuemapid', $item) && array_key_exists($item['valuemapid'], $valuemaps)) {
+ $item['valuemap'] = array_intersect_key($valuemaps[$item['valuemapid']],
+ array_flip($options['selectValueMap'])
+ );
+ }
+ }
+ unset($item);
+ }
+
+ if (!$options['countOutput'] && $this->outputIsRequested('parameters', $options['output'])) {
+ $item_parameters = DBselect(
+ 'SELECT ip.itemid,ip.name,ip.value'.
+ ' FROM item_parameter ip'.
+ ' WHERE '.dbConditionInt('ip.itemid', array_keys($result))
+ );
+
+ foreach ($result as &$item) {
+ $item['parameters'] = [];
+ }
+ unset($item);
+
+ while ($row = DBfetch($item_parameters)) {
+ $result[$row['itemid']]['parameters'][] = [
+ 'name' => $row['name'],
+ 'value' => $row['value']
+ ];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate items with type ITEM_TYPE_DEPENDENT for create or update operation.
+ *
+ * @param array $items
+ * @param string $items[]['itemid'] (mandatory for updated items and item prototypes)
+ * @param string $items[]['hostid']
+ * @param int $items[]['type']
+ * @param string $items[]['master_itemid'] (mandatory for ITEM_TYPE_DEPENDENT)
+ * @param int $items[]['flags'] (mandatory for items)
+ *
+ * @throws APIException for invalid data.
+ */
+ protected function validateDependentItems(array $items) {
+ $dep_items = [];
+ $upd_itemids = [];
+
+ foreach ($items as $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if ($this instanceof CDiscoveryRule || $this instanceof CItemPrototype
+ || $item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
+ $dep_items[] = $item;
+ }
+
+ if (array_key_exists('itemid', $item)) {
+ $upd_itemids[] = $item['itemid'];
+ }
+ }
+ }
+
+ if (!$dep_items) {
+ return;
+ }
+
+ if ($this instanceof CItemPrototype && $upd_itemids) {
+ $db_links = DBselect(
+ 'SELECT id.itemid,id.parent_itemid AS ruleid'.
+ ' FROM item_discovery id'.
+ ' WHERE '.dbConditionId('id.itemid', $upd_itemids)
+ );
+
+ $links = [];
+
+ while ($db_link = DBfetch($db_links)) {
+ $links[$db_link['itemid']] = $db_link['ruleid'];
+ }
+
+ foreach ($dep_items as &$dep_item) {
+ if (array_key_exists('itemid', $dep_item)) {
+ $dep_item['ruleid'] = $links[$dep_item['itemid']];
+ }
+ }
+ unset($dep_item);
+ }
+
+ $master_itemids = [];
+
+ foreach ($dep_items as $dep_item) {
+ $master_itemids[$dep_item['master_itemid']] = true;
+ }
+
+ $master_items = [];
+
+ // Fill relations array by master items (item prototypes). Discovery rule should not be master item.
+ do {
+ if ($this instanceof CItemPrototype) {
+ $db_master_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
+ ' FROM items i'.
+ ' LEFT JOIN item_discovery id'.
+ ' ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
+ );
+ }
+ // CDiscoveryRule, CItem
+ else {
+ $db_master_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL])
+ );
+ }
+
+ while ($db_master_item = DBfetch($db_master_items)) {
+ $master_items[$db_master_item['itemid']] = $db_master_item;
+
+ unset($master_itemids[$db_master_item['itemid']]);
+ }
+
+ if ($master_itemids) {
+ reset($master_itemids);
+
+ self::exception(ZBX_API_ERROR_PERMISSIONS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid',
+ _s('Item "%1$s" does not exist or you have no access to this item', key($master_itemids))
+ )
+ );
+ }
+
+ $master_itemids = [];
+
+ foreach ($master_items as $master_item) {
+ if ($master_item['master_itemid'] != 0
+ && !array_key_exists($master_item['master_itemid'], $master_items)) {
+ $master_itemids[$master_item['master_itemid']] = true;
+ }
+ }
+ } while ($master_itemids);
+
+ foreach ($dep_items as $dep_item) {
+ $master_item = $master_items[$dep_item['master_itemid']];
+
+ if ($dep_item['hostid'] != $master_item['hostid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('"hostid" of dependent item and master item should match')
+ ));
+ }
+
+ if ($this instanceof CItemPrototype && $master_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ && $dep_item['ruleid'] != $master_item['ruleid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('"ruleid" of dependent item and master item should match')
+ ));
+ }
+
+ if (array_key_exists('itemid', $dep_item)) {
+ $master_itemid = $dep_item['master_itemid'];
+
+ while ($master_itemid != 0) {
+ if ($master_itemid == $dep_item['itemid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('circular item dependency is not allowed')
+ ));
+ }
+
+ $master_itemid = $master_items[$master_itemid]['master_itemid'];
+ }
+ }
+ }
+
+ // Fill relations array by dependent items (item prototypes).
+ $root_itemids = [];
+
+ foreach ($master_items as $master_item) {
+ if ($master_item['master_itemid'] == 0) {
+ $root_itemids[] = $master_item['itemid'];
+ }
+ }
+
+ $dependent_items = [];
+
+ foreach ($dep_items as $dep_item) {
+ if (array_key_exists('itemid', $dep_item)) {
+ $dependent_items[$dep_item['master_itemid']][] = $dep_item['itemid'];
+ }
+ }
+
+ $master_itemids = $root_itemids;
+
+ do {
+ $sql = 'SELECT i.master_itemid,i.itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.master_itemid', $master_itemids);
+ if ($upd_itemids) {
+ $sql .= ' AND '.dbConditionId('i.itemid', $upd_itemids, true); // Exclude updated items.
+ }
+
+ $db_items = DBselect($sql);
+
+ while ($db_item = DBfetch($db_items)) {
+ $dependent_items[$db_item['master_itemid']][] = $db_item['itemid'];
+ }
+
+ $_master_itemids = $master_itemids;
+ $master_itemids = [];
+
+ foreach ($_master_itemids as $master_itemid) {
+ if (array_key_exists($master_itemid, $dependent_items)) {
+ $master_itemids = array_merge($master_itemids, $dependent_items[$master_itemid]);
+ }
+ }
+ } while ($master_itemids);
+
+ foreach ($dep_items as $dep_item) {
+ if (!array_key_exists('itemid', $dep_item)) {
+ $dependent_items[$dep_item['master_itemid']][] = false;
+ }
+ }
+
+ foreach ($root_itemids as $root_itemid) {
+ self::checkDependencyDepth($dependent_items, $root_itemid);
+ }
+ }
+
+ /**
+ * Validate depth and amount of elements in the tree of the dependent items.
+ *
+ * @param array $dependent_items
+ * @param string $dependent_items[<master_itemid>][] List if the dependent item IDs ("false" for new items)
+ * by master_itemid.
+ * @param string $root_itemid ID of the item being checked.
+ * @param int $level Current dependency level.
+ *
+ * @throws APIException for invalid data.
+ */
+ private static function checkDependencyDepth(array $dependent_items, $root_itemid, $level = 0) {
+ $count = 0;
+
+ if (array_key_exists($root_itemid, $dependent_items)) {
+ if (++$level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('maximum number of dependency levels reached')
+ ));
+ }
+
+ foreach ($dependent_items[$root_itemid] as $master_itemid) {
+ $count++;
+
+ if ($master_itemid !== false) {
+ $count += self::checkDependencyDepth($dependent_items, $master_itemid, $level);
+ }
+ }
+
+ if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('maximum dependent items count reached')
+ ));
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Converts headers field text to hash with header name as key.
+ *
+ * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ *
+ * @return array
+ */
+ protected function headersStringToArray($headers) {
+ $result = [];
+
+ foreach (explode("\r\n", $headers) as $header) {
+ $header = explode(': ', $header, 2);
+
+ if (count($header) == 2) {
+ $result[$header[0]] = $header[1];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts headers fields hash to string.
+ *
+ * @param array $headers Array of headers where key is header name.
+ *
+ * @return string
+ */
+ protected function headersArrayToString(array $headers) {
+ $result = [];
+
+ foreach ($headers as $k => $v) {
+ $result[] = $k.': '.$v;
+ }
+
+ return implode("\r\n", $result);
+ }
+
+ /**
+ * Validate item with type ITEM_TYPE_HTTPAGENT.
+ *
+ * @param array $item Array of item fields.
+ * @param array $db_item Array of item database fields for update action or empty array for create action.
+ *
+ * @throws APIException for invalid data.
+ */
+ protected function validateHTTPCheck(array $item, array $db_item) {
+ $rules = [
+ 'timeout' => [
+ 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
+ ? API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
+ : API_NOT_EMPTY | API_ALLOW_USER_MACRO,
+ 'in' => '1:'.SEC_PER_MIN
+ ],
+ 'url' => [
+ 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('items', 'url')
+ ],
+ 'status_codes' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'status_codes')
+ ],
+ 'follow_redirects' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])
+ ],
+ 'post_type' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])
+ ],
+ 'http_proxy' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')
+ ],
+ 'headers' => [
+ 'type' => API_STRINGS_UTF8
+ ],
+ 'retrieve_mode' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
+ HTTPTEST_STEP_RETRIEVE_MODE_BOTH
+ ])
+ ],
+ 'request_method' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD
+ ])
+ ],
+ 'output_format' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])
+ ],
+ 'allow_traps' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])
+ ],
+ 'ssl_cert_file' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')
+ ],
+ 'ssl_key_file' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')
+ ],
+ 'ssl_key_password' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')
+ ],
+ 'verify_peer' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])
+ ],
+ 'verify_host' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])
+ ],
+ 'authtype' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ])
+ ]
+ ];
+
+ $data = $item + $db_item;
+
+ if (array_key_exists('authtype', $data)
+ && ($data['authtype'] == HTTPTEST_AUTH_BASIC || $data['authtype'] == HTTPTEST_AUTH_NTLM
+ || $data['authtype'] == HTTPTEST_AUTH_KERBEROS || $data['authtype'] == HTTPTEST_AUTH_DIGEST)) {
+ $rules += [
+ 'username' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ 'password' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')]
+ ];
+ }
+
+ // Strict validation for 'retrieve_mode' only for create action.
+ if (array_key_exists('request_method', $data) && $data['request_method'] == HTTPCHECK_REQUEST_HEAD
+ && array_key_exists('retrieve_mode', $item)) {
+ $rules['retrieve_mode']['in'] = (string) HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ }
+
+ if (array_key_exists('post_type', $data)
+ && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
+ $rules['posts'] = [
+ 'type' => API_STRING_UTF8,
+ 'length' => DB::getFieldLength('items', 'posts')
+ ];
+ }
+
+ if (array_key_exists('templateid', $data) && $data['templateid']) {
+ $rules['interfaceid'] = [
+ 'type' => API_ID, 'flags' => API_REQUIRED | API_NOT_EMPTY
+ ];
+
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ unset($rules['interfaceid']['flags']);
+ }
+ }
+
+ if (array_key_exists('trapper_hosts', $item) && $item['trapper_hosts'] !== ''
+ && (!array_key_exists('allow_traps', $data) || $data['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', _('should be empty'))
+ );
+ }
+
+ // Keep values only for fields with defined validation rules.
+ $data = array_intersect_key($data, $rules);
+
+ if (!CApiInputValidator::validate(['type' => API_OBJECT, 'fields' => $rules], $data, '', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ if (array_key_exists('query_fields', $item)) {
+ if (!is_array($item['query_fields'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('an array is expected'))
+ );
+ }
+
+ foreach ($item['query_fields'] as $v) {
+ if (!is_array($v) || count($v) > 1 || key($v) === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('nonempty key and value pair expected'))
+ );
+ }
+ }
+
+ if (strlen(json_encode($item['query_fields'])) > DB::getFieldLength('items', 'query_fields')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'query_fields',
+ _('cannot convert to JSON, result value too long')
+ ));
+ }
+ }
+
+ if (array_key_exists('headers', $item)) {
+ if (!is_array($item['headers'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'headers', _('an array is expected'))
+ );
+ }
+
+ foreach ($item['headers'] as $k => $v) {
+ if (trim($k) === '' || !is_string($v) || $v === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'headers', _('nonempty key and value pair expected'))
+ );
+ }
+ }
+ }
+
+ if (array_key_exists('status_codes', $item) && $item['status_codes']) {
+ $ranges_parser = new CRangesParser([
+ 'usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+
+ if ($ranges_parser->parse($item['status_codes']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value "%1$s" for "%2$s" field.', $item['status_codes'], 'status_codes')
+ );
+ }
+ }
+
+ if ((array_key_exists('post_type', $item) || array_key_exists('posts', $item))
+ && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
+ $posts = array_key_exists('posts', $data) ? $data['posts'] : '';
+ libxml_use_internal_errors(true);
+
+ if ($data['post_type'] == ZBX_POSTTYPE_XML
+ && simplexml_load_string($posts, null, LIBXML_IMPORT_FLAGS) === false) {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+
+ if (!$errors) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('XML is expected'))
+ );
+ }
+ else {
+ $error = reset($errors);
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'posts',
+ _s('%1$s [Line: %2$s | Column: %3$s]', '('.$error->code.') '.trim($error->message),
+ $error->line, $error->column
+ )));
+ }
+ }
+
+ if ($data['post_type'] == ZBX_POSTTYPE_JSON) {
+ if (trim($posts, " \r\n") === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
+ );
+ }
+
+ $types = [
+ 'usermacros' => true,
+ 'macros_n' => [
+ '{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}',
+ '{ITEM.KEY}'
+ ]
+ ];
+
+ if ($this instanceof CItemPrototype) {
+ $types['lldmacros'] = true;
+ }
+
+ $matches = (new CMacrosResolverGeneral)->getMacroPositions($posts, $types);
+
+ $shift = 0;
+
+ foreach ($matches as $pos => $substr) {
+ $posts = substr_replace($posts, '1', $pos + $shift, strlen($substr));
+ $shift = $shift + 1 - strlen($substr);
+ }
+
+ json_decode($posts);
+
+ if (json_last_error()) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove NCLOB value type fields from resulting query SELECT part if DISTINCT will be used.
+ *
+ * @param string $table_name Table name.
+ * @param string $table_alias Table alias value.
+ * @param array $options Array of query options.
+ * @param array $sql_parts Array of query parts already initialized from $options.
+ *
+ * @return array The resulting SQL parts array.
+ */
+ protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts) {
+ if (!$options['countOutput'] && self::dbDistinct($sql_parts)) {
+ $schema = $this->getTableSchema();
+ $nclob_fields = [];
+
+ foreach ($schema['fields'] as $field_name => $field) {
+ if ($field['type'] == DB::FIELD_TYPE_NCLOB
+ && $this->outputIsRequested($field_name, $options['output'])) {
+ $nclob_fields[] = $field_name;
+ }
+ }
+
+ if ($nclob_fields) {
+ $output = ($options['output'] === API_OUTPUT_EXTEND)
+ ? array_keys($schema['fields'])
+ : $options['output'];
+
+ $options['output'] = array_diff($output, $nclob_fields);
+ }
+ }
+
+ return parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
+ }
+
+ /**
+ * Add NCLOB type fields if there was DISTINCT in query.
+ *
+ * @param array $options Array of query options.
+ * @param array $result Query results.
+ *
+ * @return array The result array with added NCLOB fields.
+ */
+ protected function addNclobFieldValues(array $options, array $result): array {
+ $schema = $this->getTableSchema();
+ $nclob_fields = [];
+
+ foreach ($schema['fields'] as $field_name => $field) {
+ if ($field['type'] == DB::FIELD_TYPE_NCLOB && $this->outputIsRequested($field_name, $options['output'])) {
+ $nclob_fields[] = $field_name;
+ }
+ }
+
+ if (!$nclob_fields) {
+ return $result;
+ }
+
+ $pk = $schema['key'];
+ $options = [
+ 'output' => $nclob_fields,
+ 'filter' => [$pk => array_keys($result)]
+ ];
+
+ $db_items = DBselect(DB::makeSql($this->tableName, $options));
+
+ while ($db_item = DBfetch($db_items)) {
+ $result[$db_item[$pk]] += $db_item;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Update item tags.
+ *
+ * @param array $items
+ * @param string $items[]['itemid']
+ * @param array $items[]['tags']
+ * @param string $items[]['tags'][]['tag']
+ * @param string $items[]['tags'][]['value']
+ */
+ protected function updateItemTags(array $items): void {
+ $items = array_filter($items, function ($item) {
+ return array_key_exists('tags', $item);
+ });
+
+ // Select tags from database.
+ $db_tags = DBselect(
+ 'SELECT itemtagid, itemid, tag, value'.
+ ' FROM item_tag'.
+ ' WHERE '.dbConditionInt('itemid', array_keys($items))
+ );
+
+ array_walk($items, function (&$item) {
+ $item['db_tags'] = [];
+ });
+
+ while ($db_tag = DBfetch($db_tags)) {
+ $items[$db_tag['itemid']]['db_tags'][] = $db_tag;
+ }
+
+ // Find which tags must be added/deleted.
+ $new_tags = [];
+ $del_tagids = [];
+ foreach ($items as $item) {
+ CArrayHelper::sort($item['tags'], ['tag', 'value']);
+
+ foreach ($item['db_tags'] as $del_tag_key => $tag_delete) {
+ foreach ($item['tags'] as $new_tag_key => $tag_add) {
+ if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
+ unset($item['db_tags'][$del_tag_key], $item['tags'][$new_tag_key]);
+ continue 2;
+ }
+ }
+ }
+
+ $del_tagids = array_merge($del_tagids, array_column($item['db_tags'], 'itemtagid'));
+
+ foreach ($item['tags'] as $tag_add) {
+ $tag_add['itemid'] = $item['itemid'];
+ $new_tags[] = $tag_add;
+ }
+ }
+
+ if ($del_tagids) {
+ DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ }
+ if ($new_tags) {
+ DB::insert('item_tag', $new_tags);
+ }
+ }
+
+ /**
+ * Record item tags into database.
+ *
+ * @param array $items
+ * @param array $items[]['tags']
+ * @param string $items[]['tags'][]['tag']
+ * @param string $items[]['tags'][]['value']
+ * @param int $items[]['itemid']
+ */
+ protected function createItemTags(array $items): void {
+ $new_tags = [];
+ foreach ($items as $key => $item) {
+ if (array_key_exists('tags', $item)) {
+ foreach ($item['tags'] as $tag) {
+ $tag['itemid'] = $item['itemid'];
+ $new_tags[] = $tag;
+ }
+ }
+ }
+
+ if ($new_tags) {
+ DB::insert('item_tag', $new_tags);
+ }
+ }
+
+ /**
+ * Check that valuemap belong to same host as item.
+ *
+ * @param array $items
+ */
+ protected function validateValueMaps(array $items): void {
+ $valuemapids_by_hostid = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0) {
+ $valuemapids_by_hostid[$item['hostid']][$item['valuemapid']] = true;
+ }
+ }
+
+ $sql_where = [];
+ foreach ($valuemapids_by_hostid as $hostid => $valuemapids) {
+ $sql_where[] = '(vm.hostid='.zbx_dbstr($hostid).' AND '.
+ dbConditionId('vm.valuemapid', array_keys($valuemapids)).')';
+ }
+
+ if ($sql_where) {
+ $result = DBselect(
+ 'SELECT vm.valuemapid,vm.hostid'.
+ ' FROM valuemap vm'.
+ ' WHERE '.implode(' OR ', $sql_where)
+ );
+ while ($row = DBfetch($result)) {
+ unset($valuemapids_by_hostid[$row['hostid']][$row['valuemapid']]);
+
+ if (!$valuemapids_by_hostid[$row['hostid']]) {
+ unset($valuemapids_by_hostid[$row['hostid']]);
+ }
+ }
+
+ if ($valuemapids_by_hostid) {
+ $hostid = key($valuemapids_by_hostid);
+ $valuemapid = key($valuemapids_by_hostid[$hostid]);
+
+ $host_row = DBfetch(DBselect('SELECT h.host FROM hosts h WHERE h.hostid='.zbx_dbstr($hostid)));
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Valuemap with ID "%1$s" is not available on "%2$s".',
+ $valuemapid, $host_row['host']
+ ));
+ }
+ }
+ }
+
+ /**
+ * Normalize preprocessing step parameters.
+ *
+ * @param array $preprocessing Preprocessing steps.
+ * @param string $preprocessing[<num>]['params'] Preprocessing step parameters.
+ * @param int $preprocessing[<num>]['type'] Preprocessing step type.
+ *
+ * @return array
+ */
+ protected function normalizeItemPreprocessingSteps(array $preprocessing): array {
+ foreach ($preprocessing as &$step) {
+ $step['params'] = str_replace("\r\n", "\n", $step['params']);
+ $params = explode("\n", $step['params']);
+
+ switch ($step['type']) {
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ if (!array_key_exists(2, $params)) {
+ $params[2] = '';
+ }
+ break;
+ }
+
+ $step['params'] = implode("\n", $params);
+ }
+ unset($step);
+
+ return $preprocessing;
+ }
+}
diff --git a/ui/include/classes/api/services/CItemPrototype.php b/ui/include/classes/api/services/CItemPrototype.php
index 8376a8e4eee..9a593ce9747 100644
--- a/ui/include/classes/api/services/CItemPrototype.php
+++ b/ui/include/classes/api/services/CItemPrototype.php
@@ -29,39 +29,60 @@ class CItemPrototype extends CItemGeneral {
protected $sortColumns = ['itemid', 'name', 'key_', 'delay', 'history', 'trends', 'type', 'status', 'discover'];
/**
- * Define a set of supported pre-processing rules.
- *
- * @var array
+ * @inheritDoc
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC, ZBX_PREPROC_DELTA_VALUE,
+ ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE,
+ ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON,
+ ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_VALUE,
+ ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT, ZBX_PREPROC_PROMETHEUS_PATTERN,
+ ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE,
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX,
+ ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML,
+ ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
+ ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE
+ ];
+
+ /**
+ * @inheritDoc
*/
- const SUPPORTED_PREPROCESSING_TYPES = [ZBX_PREPROC_REGSUB, ZBX_PREPROC_TRIM, ZBX_PREPROC_RTRIM,
- ZBX_PREPROC_LTRIM, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_DELTA_VALUE,
- ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_REGSUB, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH,
ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
- ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
- ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
];
- public function __construct() {
- parent::__construct();
-
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_EXISTS_TEMPLATE => _('Item prototype "%1$s" already exists on "%2$s", inherited from another template.'),
- self::ERROR_EXISTS => _('Item prototype "%1$s" already exists on "%2$s".'),
- self::ERROR_INVALID_KEY => _('Invalid key "%1$s" for item prototype "%2$s" on "%3$s": %4$s.')
- ]);
- }
+ /**
+ * @inheritDoc
+ */
+ protected const SUPPORTED_ITEM_TYPES = [
+ ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
/**
- * Define a set of supported item types.
- *
- * @var array
+ * @inheritDoc
*/
- const SUPPORTED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
- ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT,
- ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ protected const VALUE_TYPE_FIELD_NAMES = [
+ ITEM_VALUE_TYPE_FLOAT => ['units', 'trends', 'valuemapid'],
+ ITEM_VALUE_TYPE_STR => ['valuemapid'],
+ ITEM_VALUE_TYPE_LOG => ['logtimefmt'],
+ ITEM_VALUE_TYPE_UINT64 => ['units', 'trends', 'valuemapid'],
+ ITEM_VALUE_TYPE_TEXT => []
];
/**
@@ -306,7 +327,7 @@ class CItemPrototype extends CItemGeneral {
}
if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersStringToArray($item['headers']);
+ $item['headers'] = self::headersStringToArray($item['headers']);
}
}
unset($item);
@@ -337,403 +358,1009 @@ class CItemPrototype extends CItemGeneral {
}
/**
- * Check item prototype data and set flags field.
+ * @param array $items
+ *
+ * @return array
+ */
+ public function create(array $items): array {
+ self::validateCreate($items);
+
+ self::createForce($items);
+ self::inherit($items);
+
+ return ['itemids' => array_column($items, 'itemid')];
+ }
+
+ /**
+ * @param array $items
*
- * @param array $items an array of items passed by reference
- * @param bool $update
+ * @throws APIException
+ */
+ protected static function validateCreate(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'fields' => [
+ 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ self::checkHostsAndTemplates($items, $db_hosts, $db_templates);
+ self::addHostStatus($items, $db_hosts, $db_templates);
+ self::addFlags($items, ZBX_FLAG_DISCOVERY_PROTOTYPE);
+
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'uniq' => [['uuid'], ['hostid', 'key_']], 'fields' => [
+ 'host_status' => ['type' => API_ANY],
+ 'flags' => ['type' => API_ANY],
+ 'uuid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_TEMPLATE])], 'type' => API_UUID],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'uuid')]
+ ]],
+ 'hostid' => ['type' => API_ANY],
+ 'ruleid' => ['type' => API_ID, 'flags' => API_REQUIRED],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED | API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0', 'default' => 0]
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ self::validateByType(array_keys($api_input_rules['fields']), $items);
+
+ self::checkAndAddUuid($items);
+ self::checkDuplicates($items);
+ self::checkDiscoveryRules($items);
+ self::checkValueMaps($items);
+ self::checkHostInterfaces($items);
+ self::checkDependentItems($items);
+ }
+
+ /**
+ * @param array $items
*/
- protected function checkInput(array &$items, $update = false) {
- parent::checkInput($items, $update);
+ public static function createForce(array &$items): void {
+ $itemids = DB::insert('items', $items);
+
+ $ins_items_discovery = [];
+ $host_statuses = [];
+ $flags = [];
- // set proper flags to divide normal and discovered items in future processing
foreach ($items as &$item) {
- $item['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
+ $item['itemid'] = array_shift($itemids);
+
+ if ($item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $ins_items_discovery[] = [
+ 'itemid' => $item['itemid'],
+ 'parent_itemid' => $item['ruleid']
+ ];
+ }
+
+ $host_statuses[] = $item['host_status'];
+ $flags[] = $item['flags'];
+ unset($item['host_status'], $item['flags']);
+ }
+ unset($item);
+
+ if ($ins_items_discovery) {
+ DB::insertBatch('item_discovery', $ins_items_discovery);
+ }
+
+ self::updateParameters($items);
+ self::updatePreprocessing($items);
+ self::updateTags($items);
+
+ self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_ITEM_PROTOTYPE, $items);
+
+ foreach ($items as &$item) {
+ $item['host_status'] = array_shift($host_statuses);
+ $item['flags'] = array_shift($flags);
}
unset($item);
}
/**
- * Create item prototype.
- *
* @param array $items
*
* @return array
*/
- public function create($items) {
- $items = zbx_toArray($items);
+ public function update(array $items): array {
+ $this->validateUpdate($items, $db_items);
- $this->checkInput($items);
+ $itemids = array_column($items, 'itemid');
- foreach ($items as $key => $item) {
- $items[$key]['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
- unset($items[$key]['itemid']);
- }
+ self::updateForce($items, $db_items);
+ self::inherit($items, $db_items);
- // Validate item prototype status and discover status fields.
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
- 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])]
+ return ['itemids' => $itemids];
+ }
+
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected function validateUpdate(array &$items, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'uniq' => [['itemid']], 'fields' => [
+ 'itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
]];
- foreach ($items as $key => $item) {
- $item = array_intersect_key($item, $api_input_rules['fields']);
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
- if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($key + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ $count = $this->get([
+ 'countOutput' => true,
+ 'itemids' => array_column($items, 'itemid'),
+ 'editable' => true
+ ]);
+
+ if ($count != count($items)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
}
- $this->validateDependentItems($items);
+ /*
+ * The fields "headers" and "query_fields" in API are arrays, but there is necessary to get the values of these
+ * fields as they stored in database.
+ */
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_column($items, 'itemid'),
+ 'preservekeys' => true
+ ]);
- foreach ($items as &$item) {
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $item)) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ self::addInternalFields($db_items);
- if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ foreach ($items as $i => &$item) {
+ $db_item = $db_items[$item['itemid']];
+
+ if ($db_item['templateid'] != 0) {
+ $api_input_rules = ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getInheritedValidationRules();
}
else {
- $item['query_fields'] = '';
- $item['headers'] = '';
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getValidationRules();
}
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
}
unset($item);
- $this->createReal($items);
- $this->inherit($items);
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['type', 'key_']);
+
+ self::validateByType(array_keys($api_input_rules['fields']), $items, $db_items);
+
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['hostid', 'host_status', 'flags', 'ruleid']);
+
+ self::validateUniqueness($items);
+
+ self::addAffectedObjects($items, $db_items);
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::checkDuplicates($items, $db_items);
+ self::checkValueMaps($items, $db_items);
+ self::checkHostInterfaces($items, $db_items);
+ self::checkDependentItems($items, $db_items);
}
- protected function createReal(&$items) {
- foreach ($items as &$item) {
- if ($item['type'] != ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = null;
+ /**
+ * @inheritDoc
+ */
+ public static function getPreprocessingValidationRules(int $flags = 0x00): array {
+ return parent::getPreprocessingValidationRules(API_ALLOW_LLD_MACRO);
+ }
+
+ /**
+ * @return array
+ */
+ private static function getValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getInheritedValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'value_type' => ['type' => API_ANY],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected static function addInternalFields(array &$db_items): void {
+ $result = DBselect(
+ 'SELECT i.itemid,i.hostid,i.templateid,i.flags,h.status AS host_status,id.parent_itemid AS ruleid'.
+ ' FROM items i,hosts h,item_discovery id'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND i.itemid=id.itemid'.
+ ' AND '.dbConditionId('i.itemid', array_keys($db_items))
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_items[$row['itemid']] += $row;
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ public static function updateForce(array &$items, array &$db_items): void {
+ // Helps to avoid deadlocks.
+ CArrayHelper::sort($items, ['itemid'], ZBX_SORT_DOWN);
+
+ self::addFieldDefaultsByType($items, $db_items);
+
+ $upd_items = [];
+ $upd_itemids = [];
+
+ $internal_fields = array_flip(['itemid', 'type', 'key_', 'hostid', 'flags', 'host_status']);
+ $nested_object_fields = array_flip(['tags', 'preprocessing', 'parameters']);
+
+ foreach ($items as $i => &$item) {
+ $upd_item = DB::getUpdatedValues('items', $item, $db_items[$item['itemid']]);
+
+ if ($upd_item) {
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $item['itemid']]
+ ];
+
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item = array_intersect_key($item,
+ array_flip(['authtype']) + $internal_fields + $upd_item + $nested_object_fields
+ );
+ }
+ else {
+ $item = array_intersect_key($item, $internal_fields + $upd_item + $nested_object_fields);
+ }
+
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ $item = array_intersect_key($item, $internal_fields + $nested_object_fields);
}
}
unset($item);
- $itemids = DB::insert('items', $items);
-
- $insertItemDiscovery = [];
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
- $insertItemDiscovery[] = [
- 'itemid' => $items[$key]['itemid'],
- 'parent_itemid' => $item['ruleid']
- ];
+ if ($upd_items) {
+ DB::update('items', $upd_items);
}
- DB::insertBatch('item_discovery', $insertItemDiscovery);
- $this->createItemParameters($items, $itemids);
- $this->createItemPreprocessing($items);
- $this->createItemTags($items);
+ self::updateTags($items, $db_items, $upd_itemids);
+ self::updatePreprocessing($items, $db_items, $upd_itemids);
+ self::updateParameters($items, $db_items, $upd_itemids);
+ self::updateDiscoveredItems($items, $db_items);
+
+ $items = array_intersect_key($items, $upd_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($upd_itemids));
+
+ self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_ITEM_PROTOTYPE, $items, $db_items);
}
- protected function updateReal(array $items) {
- CArrayHelper::sort($items, ['itemid']);
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ private static function updateDiscoveredItems(array $item_prototypes, array $db_item_prototypes): void {
+ foreach ($item_prototypes as $i => $item_prototype) {
+ if (!in_array($item_prototype['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ || !array_key_exists('update_discovered_items', $db_item_prototypes[$item_prototype['itemid']])) {
+ unset($item_prototype[$i]);
+ continue;
+ }
+ }
- $data = [];
- foreach ($items as $item) {
- $data[] = ['values' => $item, 'where'=> ['itemid' => $item['itemid']]];
+ if (!$item_prototypes) {
+ return;
}
- $result = DB::update('items', $data);
- if (!$result) {
- self::exception(ZBX_API_ERROR_PARAMETERS, 'DBerror');
+ $result = DBselect(
+ 'SELECT id.itemid,i.name,i.valuemapid'.
+ ' FROM item_discovery id,items i'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('id.parent_itemid', array_column($item_prototypes, 'itemid'))
+ );
+
+ $items = [];
+ $db_items = [];
+
+ while ($row = DBfetch($result)) {
+ $items[] = [
+ 'itemid' => $row['itemid'],
+ 'valuemapid' => 0
+ ];
+
+ $db_items[$row['itemid']] = $row;
}
- $this->updateItemParameters($items);
- $this->updateItemPreprocessing($items);
- $this->updateItemTags($items);
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
}
/**
- * Update ItemPrototype.
+ * @param array $itemids
*
- * @param array $items
+ * @throws APIException
*
* @return array
*/
- public function update($items) {
- $items = zbx_toArray($items);
+ public function delete(array $itemids): array {
+ $this->validateDelete($itemids, $db_items);
- $this->checkInput($items, true);
+ self::deleteForce($db_items);
- // Validate item prototype status and discover status fields.
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
- 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])]
- ]];
+ return ['prototypeids' => $itemids];
+ }
- foreach ($items as $key => $item) {
- $items[$key]['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
+ /**
+ * @param array $itemids
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ private function validateDelete(array $itemids, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
- $item = array_intersect_key($item, $api_input_rules['fields']);
- if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($key + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
$db_items = $this->get([
- 'output' => ['type', 'master_itemid', 'authtype', 'allow_traps', 'retrieve_mode', 'value_type'],
- 'itemids' => zbx_objectValues($items, 'itemid'),
+ 'output' => ['itemid', 'name', 'templateid'],
+ 'itemids' => $itemids,
'editable' => true,
'preservekeys' => true
]);
- $items = $this->extendFromObjects(zbx_toHash($items, 'itemid'), $db_items, ['type', 'authtype',
- 'master_itemid', 'value_type'
+ if (count($db_items) != count($itemids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ foreach ($itemids as $i => $itemid) {
+ if ($db_items[$itemid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('cannot delete inherited item prototype')
+ ));
+ }
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array $hostids
+ */
+ public static function linkTemplateObjects(array $templateids, array $hostids): void {
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['interfaceid', 'parameters'])),
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hostid' => $templateids
+ ],
+ 'preservekeys' => true
]);
- $this->validateDependentItems($items);
-
- $defaults = DB::getDefaults('items');
- $clean = [
- ITEM_TYPE_HTTPAGENT => [
- 'url' => '',
- 'query_fields' => '',
- 'timeout' => $defaults['timeout'],
- 'status_codes' => $defaults['status_codes'],
- 'follow_redirects' => $defaults['follow_redirects'],
- 'request_method' => $defaults['request_method'],
- 'allow_traps' => $defaults['allow_traps'],
- 'post_type' => $defaults['post_type'],
- 'http_proxy' => '',
- 'headers' => '',
- 'retrieve_mode' => $defaults['retrieve_mode'],
- 'output_format' => $defaults['output_format'],
- 'ssl_key_password' => '',
- 'verify_peer' => $defaults['verify_peer'],
- 'verify_host' => $defaults['verify_host'],
- 'ssl_cert_file' => '',
- 'ssl_key_file' => '',
- 'posts' => ''
- ]
- ];
+ if (!$db_items) {
+ return;
+ }
+
+ self::addInternalFields($db_items);
+
+ $items = [];
+
+ foreach ($db_items as $db_item) {
+ $item = array_intersect_key($db_item, array_flip(['itemid', 'type']));
+
+ if ($db_item['type'] == ITEM_TYPE_SCRIPT) {
+ $item += ['parameters' => []];
+ }
+
+ $items[] = $item + [
+ 'preprocessing' => [],
+ 'tags' => []
+ ];
+ }
+
+ self::addAffectedObjects($items, $db_items);
+
+ $items = array_values($db_items);
foreach ($items as &$item) {
- $type_change = ($item['type'] != $db_items[$item['itemid']]['type']);
+ if (array_key_exists('parameters', $item)) {
+ $item['parameters'] = array_values($item['parameters']);
+ }
+
+ $item['preprocessing'] = array_values($item['preprocessing']);
+ $item['tags'] = array_values($item['tags']);
+ }
+ unset($item);
- if ($item['type'] != ITEM_TYPE_DEPENDENT && $db_items[$item['itemid']]['master_itemid'] != 0) {
- $item['master_itemid'] = 0;
+ self::inherit($items, [], $hostids);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void {
+ $tpl_links = self::getTemplateLinks($items, $hostids);
+
+ if ($hostids === null) {
+ self::filterObjectsToInherit($items, $db_items, $tpl_links);
+
+ if (!$items) {
+ return;
}
+ }
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = array_merge($item, $clean[ITEM_TYPE_HTTPAGENT]);
+ self::checkDoubleInheritedNames($items, $db_items, $tpl_links);
- if ($item['type'] != ITEM_TYPE_SSH) {
- $item['authtype'] = $defaults['authtype'];
- $item['username'] = '';
- $item['password'] = '';
- }
+ if ($hostids !== null && !$is_dep_items) {
+ $dep_items_to_link = [];
+
+ $item_indexes = array_flip(array_column($items, 'itemid'));
+
+ foreach ($items as $i => $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT
+ && array_key_exists($item['master_itemid'], $item_indexes)) {
+ $dep_items_to_link[$item_indexes[$item['master_itemid']]][$i] = $item;
- if ($item['type'] != ITEM_TYPE_TRAPPER) {
- $item['trapper_hosts'] = '';
+ unset($items[$i]);
}
}
+ }
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- // Clean username and password when authtype is set to HTTPTEST_AUTH_NONE.
- if ($item['authtype'] == HTTPTEST_AUTH_NONE) {
- $item['username'] = '';
- $item['password'] = '';
- }
+ $chunks = self::getInheritChunks($items, $tpl_links);
- if (array_key_exists('allow_traps', $item) && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF
- && $item['allow_traps'] != $db_items[$item['itemid']]['allow_traps']) {
- $item['trapper_hosts'] = '';
- }
+ foreach ($chunks as $chunk) {
+ $_items = array_intersect_key($items, array_flip($chunk['item_indexes']));
+ $_db_items = array_intersect_key($db_items, array_flip(array_column($_items, 'itemid')));
+ $_hostids = array_keys($chunk['hosts']);
- if (array_key_exists('query_fields', $item) && is_array($item['query_fields'])) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ self::inheritChunk($_items, $_db_items, $tpl_links, $_hostids);
+ }
- if (array_key_exists('headers', $item) && is_array($item['headers'])) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ if ($hostids !== null && !$is_dep_items) {
+ self::inheritDependentItems($dep_items_to_link, $items, $hostids);
+ }
+ }
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)
- && $db_items[$item['itemid']]['retrieve_mode'] != HTTPTEST_STEP_RETRIEVE_MODE_HEADERS) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ */
+ protected static function inheritChunk(array $items, array $db_items, array $tpl_links, array $hostids): void {
+ $items_to_link = [];
+ $items_to_update = [];
+
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ $items_to_link[] = $item;
}
else {
- $item['query_fields'] = '';
- $item['headers'] = '';
+ $items_to_update[] = $item;
}
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT) {
- if ($item['type'] != ITEM_TYPE_SSH && $item['type'] != ITEM_TYPE_DB_MONITOR
- && $item['type'] != ITEM_TYPE_TELNET && $item['type'] != ITEM_TYPE_CALCULATED) {
- $item['params'] = '';
- }
+ unset($items[$i]);
+ }
- if ($item['type'] != ITEM_TYPE_HTTPAGENT) {
- $item['timeout'] = $defaults['timeout'];
- }
+ $ins_items = [];
+ $upd_items = [];
+ $upd_db_items = [];
+
+ if ($items_to_link) {
+ $lld_links = self::getLldLinks($items_to_link);
+
+ $upd_db_items = self::getChildObjectsUsingName($items_to_link, $hostids, $lld_links);
+
+ if ($upd_db_items) {
+ $upd_items = self::getUpdChildObjectsUsingName($items_to_link, $upd_db_items);
}
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- if ($item['value_type'] != $db_items[$item['itemid']]['value_type']) {
- // Reset valuemapid when value_type is LOG or TEXT.
- $item['valuemapid'] = 0;
+ $ins_items = self::getInsChildObjects($items_to_link, $upd_db_items, $tpl_links, $hostids, $lld_links);
+ }
+
+ if ($items_to_update) {
+ $_upd_db_items = self::getChildObjectsUsingTemplateid($items_to_update, $db_items, $hostids);
+ $_upd_items = self::getUpdChildObjectsUsingTemplateid($items_to_update, $db_items, $_upd_db_items);
+
+ self::checkDuplicates($_upd_items, $_upd_db_items);
+
+ $upd_items = array_merge($upd_items, $_upd_items);
+ $upd_db_items += $_upd_db_items;
+ }
+
+ self::setChildMasterItemIds($upd_items, $ins_items, $hostids);
+
+ $edit_items = array_merge($upd_items, $ins_items);
+
+ self::checkDependentItems($edit_items, $upd_db_items, true);
+
+ self::addInterfaceIds($upd_items, $upd_db_items, $ins_items);
+
+ if ($upd_items) {
+ self::updateForce($upd_items, $upd_db_items);
+ }
+
+ if ($ins_items) {
+ self::createForce($ins_items);
+ }
+
+ self::inherit(array_merge($upd_items, $ins_items), $upd_db_items);
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getChildObjectsUsingTemplateid(array $items, array $db_items, array $hostids): array {
+ $upd_db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'filter' => [
+ 'templateid' => array_keys($db_items),
+ 'hostid' => $hostids
+ ],
+ 'preservekeys' => true
+ ]);
+
+ self::addInternalFields($upd_db_items);
+
+ if ($upd_db_items) {
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
+
+ foreach ($upd_db_items as &$upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+ $db_item = $db_items[$upd_db_item['templateid']];
+
+ if (array_key_exists('update_discovered_items', $db_item)) {
+ $upd_db_item['update_discovered_items'] = true;
}
- }
- if (array_key_exists('tags', $item)) {
- $item['tags'] = array_map(function ($tag) {
- return $tag + ['value' => ''];
- }, $item['tags']);
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type']
+ ];
+
+ $upd_item += array_intersect_key([
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ], $db_item);
+
+ $upd_items[] = $upd_item;
}
+ unset($upd_db_item);
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ self::addAffectedObjects($upd_items, $upd_db_items);
+ }
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $upd_db_items
+ *
+ * @return array
+ */
+ private static function getUpdChildObjectsUsingTemplateid(array $items, array $db_items,
+ array $upd_db_items): array {
+
+ foreach ($items as &$item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ continue;
}
+
+ $item = self::unsetNestedObjectIds($item);
}
unset($item);
- $this->updateReal($items);
- $this->inherit($items);
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+
+ $upd_items[] = array_intersect_key($upd_db_item,
+ array_flip(['itemid', 'hostid', 'templateid', 'host_status', 'ruleid'])
+ ) + $item;
+ }
+
+ return $upd_items;
}
/**
- * Delete Item prototypes.
- *
- * @param array $itemids
+ * @param array $items
+ * @param array $tpl_links
*
* @return array
*/
- public function delete(array $itemids) {
- $this->validateDelete($itemids, $db_items);
+ private static function getLldLinks(array $items): array {
+ $options = [
+ 'output' => ['templateid', 'hostid', 'itemid'],
+ 'filter' => ['templateid' => array_unique(array_column($items, 'ruleid'))]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
- CItemPrototypeManager::delete($itemids);
+ $lld_links = [];
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM_PROTOTYPE, $db_items);
+ while ($row = DBfetch($result)) {
+ $lld_links[$row['templateid']][$row['hostid']] = $row['itemid'];
+ }
- return ['prototypeids' => $itemids];
+ return $lld_links;
}
/**
- * Validates the input parameters for the delete() method.
+ * @param array $items
+ * @param array $hostids
+ * @param array $lld_links
*
- * @param array $itemids [IN/OUT]
- * @param array $db_items [OUT]
+ * @return array
+ */
+ private static function getChildObjectsUsingName(array $items, array $hostids, array $lld_links): array {
+ $result = DBselect(
+ 'SELECT i.itemid,ht.hostid,i.key_,i.templateid,i.flags,h.status AS host_status,'.
+ 'ht.templateid AS parent_hostid,id.parent_itemid AS ruleid,'.
+ dbConditionCoalesce('id.parent_itemid', 0, 'ruleid').
+ ' FROM hosts_templates ht'.
+ ' INNER JOIN items i ON ht.hostid=i.hostid'.
+ ' INNER JOIN hosts h ON ht.hostid=h.hostid'.
+ ' LEFT JOIN item_discovery id ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('ht.templateid', array_unique(array_column($items, 'hostid'))).
+ ' AND '.dbConditionString('i.key_', array_unique(array_column($items, 'key_'))).
+ ' AND '.dbConditionId('ht.hostid', $hostids)
+ );
+
+ $upd_db_items = [];
+ $parent_indexes = [];
+
+ while ($row = DBfetch($result)) {
+ foreach ($items as $i => $item) {
+ if (bccomp($row['parent_hostid'], $item['hostid']) == 0 && $row['key_'] === $item['key_']) {
+ if ($row['flags'] == $item['flags'] && $row['templateid'] == 0
+ && bccomp($row['ruleid'], $lld_links[$item['ruleid']][$row['hostid']]) == 0) {
+ $upd_db_items[$row['itemid']] = $row;
+ $parent_indexes[$row['itemid']] = $i;
+ }
+ else {
+ self::showObjectMismatchError($item, $row);
+ }
+ }
+ }
+ }
+
+ if (!$upd_db_items) {
+ return [];
+ }
+
+ $options = [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_keys($upd_db_items)
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ $upd_db_items[$row['itemid']] = $row + $upd_db_items[$row['itemid']];
+ }
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['itemid']]];
+
+ $upd_items[] = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type'],
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
*
- * @throws APIException if the input is invalid.
+ * @return array
*/
- private function validateDelete(array &$itemids, array &$db_items = null) {
- $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
- if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ private static function getUpdChildObjectsUsingName(array $items, array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['hostid']][$item['key_']] = $i;
}
+ unset($item);
- $db_items = $this->get([
- 'output' => ['itemid', 'name', 'templateid', 'flags'],
- 'itemids' => $itemids,
- 'editable' => true,
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['parent_hostid']][$upd_db_item['key_']]];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'hostid' => $upd_db_item['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $upd_db_item['host_status'],
+ 'ruleid' => $upd_db_item['ruleid']
+ ] + $item;
+
+ $upd_item += [
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+
+ $upd_items[] = $upd_item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $item
+ * @param array $upd_db_item
+ *
+ * @throws APIException
+ */
+ protected static function showObjectMismatchError(array $item, array $upd_db_item): void {
+ parent::showObjectMismatchError($item, $upd_db_item);
+
+ $target_is_host = in_array($upd_db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => [$item['hostid'], $upd_db_item['hostid']],
'preservekeys' => true
]);
- foreach ($itemids as $itemid) {
- if (!array_key_exists($itemid, $db_items)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
- }
+ $lld_rules = DB::select('items', [
+ 'output' => ['name'],
+ 'itemids' => [$item['ruleid'], $upd_db_item['ruleid']],
+ 'preservekeys' => true
+ ]);
- $db_item = $db_items[$itemid];
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" and LLD rule "%3$s" to host "%4$s", because an item prototype with the same key already belongs to LLD rule "%5$s".')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" and LLD rule "%3$s" to template "%4$s", because an item prototype with the same key already belongs to LLD rule "%5$s".');
- if ($db_item['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete templated item prototype.'));
- }
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'], $hosts[$item['hostid']]['host'],
+ $lld_rules[$item['ruleid']]['name'], $hosts[$upd_db_item['hostid']]['host'],
+ $lld_rules[$upd_db_item['ruleid']]['name']
+ ));
}
- public function syncTemplates($data) {
- $data['templateids'] = zbx_toArray($data['templateids']);
- $data['hostids'] = zbx_toArray($data['hostids']);
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ * @param array $lld_links
+ *
+ * @return array
+ */
+ private static function getInsChildObjects(array $items, array $upd_db_items, array $tpl_links, array $hostids,
+ array $lld_links): array {
+ $ins_items = [];
+
+ $upd_item_keys = [];
- $output = [];
- foreach ($this->fieldRules as $field_name => $rules) {
- if (!array_key_exists('system', $rules) && !array_key_exists('host', $rules)) {
- $output[] = $field_name;
+ foreach ($upd_db_items as $upd_db_item) {
+ $upd_item_keys[$upd_db_item['hostid']][] = $upd_db_item['key_'];
+ }
+
+ foreach ($items as $item) {
+ $item['uuid'] = '';
+ $item = self::unsetNestedObjectIds($item);
+
+ foreach ($tpl_links[$item['hostid']] as $host) {
+ if (!in_array($host['hostid'], $hostids)
+ || (array_key_exists($host['hostid'], $upd_item_keys)
+ && in_array($item['key_'], $upd_item_keys[$host['hostid']]))) {
+ continue;
+ }
+
+ $ins_items[] = [
+ 'hostid' => $host['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $host['status'],
+ 'ruleid' => $lld_links[$item['ruleid']][$host['hostid']]
+ ] + array_diff_key($item, array_flip(['itemid']));
}
}
- $tpl_items = $this->get([
- 'output' => $output,
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'hostids' => $data['templateids'],
- 'preservekeys' => true,
- 'nopermissions' => true
- ]);
+ return $ins_items;
+ }
- foreach ($tpl_items as &$tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $tpl_item) && is_array($tpl_item['query_fields'])) {
- $tpl_item['query_fields'] = $tpl_item['query_fields']
- ? json_encode($tpl_item['query_fields'])
- : '';
- }
+ /**
+ * @param array $ruleids
+ */
+ public static function unlinkTemplateObjects(array $ruleids): void {
+ $result = DBselect(
+ 'SELECT id.itemid,i.name,i.type,i.key_,i.templateid,i.uuid,i.valuemapid,i.hostid,h.status AS host_status'.
+ ' FROM item_discovery id,items i,hosts h'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND i.hostid=h.hostid'.
+ ' AND '.dbConditionId('id.parent_itemid', $ruleids).
+ ' AND '.dbConditionId('i.templateid', [0], true)
+ );
+
+ $items = [];
+ $db_items = [];
+ $i = 0;
+ $tpl_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $item = [
+ 'itemid' => $row['itemid'],
+ 'type' => $row['type'],
+ 'templateid' => 0,
+ 'host_status' => $row['host_status']
+ ];
- if (array_key_exists('headers', $tpl_item) && is_array($tpl_item['headers'])) {
- $tpl_item['headers'] = $this->headersArrayToString($tpl_item['headers']);
- }
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $item += ['uuid' => generateUuidV4()];
}
- else {
- $tpl_item['query_fields'] = '';
- $tpl_item['headers'] = '';
+
+ if ($row['valuemapid'] != 0) {
+ $item += ['valuemapid' => 0];
+ $row['update_discovered_items'] = true;
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $tpl_itemids[$i] = $row['itemid'];
+ $item += array_intersect_key($row, array_flip(['key_', 'hostid']));
+ }
}
+
+ $items[$i++] = $item;
+ $db_items[$row['itemid']] = $row;
}
- unset($tpl_item);
- $this->inherit($tpl_items, $data['hostids']);
+ if ($items) {
+ self::updateForce($items, $db_items);
+
+ if ($tpl_itemids) {
+ $items = array_intersect_key($items, $tpl_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($tpl_itemids));
- return true;
+ self::inherit($items, $db_items);
+ }
+ }
}
/**
- * Check item prototype specific fields:
- * - validate history and trends using simple interval parser, user macro parser and lld macro parser;
- * - validate item preprocessing.
+ * Check that discovery rule IDs of given items are valid.
*
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
*
- * @throws APIException if the input is invalid.
- */
- protected function checkSpecificFields(array $item, $method) {
- if (array_key_exists('history', $item)
- && !validateTimeUnit($item['history'], SEC_PER_HOUR, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true, 'lldmacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'history', $error)
- );
+ * @throws APIException
+ */
+ private static function checkDiscoveryRules(array $items): void {
+ $ruleids = array_unique(array_column($items, 'ruleid'));
+
+ $db_discovery_rules = DB::select('items', [
+ 'output' => ['hostid'],
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_RULE,
+ 'itemid' => $ruleids
+ ],
+ 'preservekeys' => true
+ ]);
+
+ if (count($db_discovery_rules) != count($ruleids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
}
- if (array_key_exists('trends', $item)
- && !validateTimeUnit($item['trends'], SEC_PER_DAY, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true, 'lldmacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trends', $error)
- );
+ foreach ($items as $item) {
+ if (bccomp($db_discovery_rules[$item['ruleid']]['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
}
}
@@ -753,7 +1380,7 @@ class CItemPrototype extends CItemGeneral {
return $sqlParts;
}
- public function addRelatedObjects(array $options, array $result) {
+ protected function addRelatedObjects(array $options, array $result) {
$result = parent::addRelatedObjects($options, $result);
$itemids = array_keys($result);
@@ -870,4 +1497,111 @@ class CItemPrototype extends CItemGeneral {
return $result;
}
+
+ /**
+ * @param array $db_items
+ */
+ public static function deleteForce(array $db_items): void {
+ self::addInheritedItems($db_items);
+ self::addDependentItems($db_items);
+
+ $del_itemids = array_keys($db_items);
+
+ // Lock item prototypes before delete to prevent server from adding new LLD elements.
+ DBselect(
+ 'SELECT NULL'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.itemid', $del_itemids).
+ ' FOR UPDATE'
+ );
+
+ self::deleteAffectedGraphPrototypes($del_itemids);
+ self::resetGraphsYAxis($del_itemids);
+
+ self::deleteDiscoveredItems($del_itemids);
+
+ self::deleteAffectedTriggers($del_itemids);
+
+ DB::delete('graphs_items', ['itemid' => $del_itemids]);
+ DB::delete('widget_field', ['value_itemid' => $del_itemids]);
+ DB::delete('item_discovery', ['itemid' => $del_itemids]);
+ DB::delete('item_parameter', ['itemid' => $del_itemids]);
+ DB::delete('item_preproc', ['itemid' => $del_itemids]);
+ DB::delete('item_tag', ['itemid' => $del_itemids]);
+ DB::update('items', [
+ 'values' => ['templateid' => 0, 'master_itemid' => 0],
+ 'where' => ['itemid' => $del_itemids]
+ ]);
+ DB::delete('items', ['itemid' => $del_itemids]);
+
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM_PROTOTYPE, $db_items);
+ }
+
+ /**
+ * Add the dependent item prototypes of the given items to the given item prototypes array.
+ *
+ * @param array $db_items
+ */
+ protected static function addDependentItems(array &$db_items): void {
+ $master_itemids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name'],
+ 'filter' => ['master_itemid' => $master_itemids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $master_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_items[$row['itemid']] = $row;
+ }
+ } while ($master_itemids);
+ }
+
+ /**
+ * Delete graph prototypes, which would remain without item prototypes after the given item prototypes deletion.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteAffectedGraphPrototypes(array $del_itemids): void {
+ $del_graphids = DBfetchColumn(DBselect(
+ 'SELECT DISTINCT gi.graphid'.
+ ' FROM graphs_items gi'.
+ ' WHERE '.dbConditionId('gi.itemid', $del_itemids).
+ ' AND NOT EXISTS ('.
+ 'SELECT NULL'.
+ ' FROM graphs_items gii,items i'.
+ ' WHERE gi.graphid=gii.graphid'.
+ ' AND gii.itemid=i.itemid'.
+ ' AND '.dbConditionId('gii.itemid', $del_itemids, true).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_PROTOTYPE]).
+ ')'
+ ), 'graphid');
+
+ if ($del_graphids) {
+ CGraphPrototypeManager::delete($del_graphids);
+ }
+ }
+
+ /**
+ * Delete discovered items of the given item prototypes.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteDiscoveredItems(array $del_itemids): void {
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT id.itemid,i.name'.
+ ' FROM item_discovery id,items i'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('id.parent_itemid', $del_itemids)
+ ), 'itemid');
+
+ if ($db_items) {
+ CItem::deleteForce($db_items);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CTemplate.php b/ui/include/classes/api/services/CTemplate.php
index 2d10d49ccd8..0c392c7f3a7 100644
--- a/ui/include/classes/api/services/CTemplate.php
+++ b/ui/include/classes/api/services/CTemplate.php
@@ -532,19 +532,24 @@ class CTemplate extends CHostGeneral {
'nopermissions' => true,
'preservekeys' => true
]);
+
if ($del_rules) {
CDiscoveryRuleManager::delete(array_keys($del_rules));
}
// delete the items
- $del_items = API::Item()->get([
- 'output' => [],
- 'templateids' => $templateids,
- 'nopermissions' => true,
+ $db_items = DB::select('items', [
+ 'output' => ['itemid', 'name'],
+ 'filter' => [
+ 'hostid' => $templateids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => CItem::SUPPORTED_ITEM_TYPES
+ ],
'preservekeys' => true
]);
- if ($del_items) {
- CItemManager::delete(array_keys($del_items));
+
+ if ($db_items) {
+ CItem::deleteForce($db_items);
}
// delete host from maps
@@ -616,15 +621,15 @@ class CTemplate extends CHostGeneral {
'operationid'=>$delOperationids
]);
- // http tests
- $delHttpTests = API::HttpTest()->get([
- 'templateids' => $templateids,
- 'output' => ['httptestid'],
- 'nopermissions' => 1,
+ // delete web scenarios
+ $db_httptests = DB::select('httptest', [
+ 'output' => ['httptestid', 'name'],
+ 'filter' => ['hostid' => $templateids],
'preservekeys' => true
]);
- if (!empty($delHttpTests)) {
- API::HttpTest()->delete(array_keys($delHttpTests), true);
+
+ if ($db_httptests) {
+ CHttpTest::deleteForce($db_httptests);
}
// Get host prototype operations from LLD overrides where this template is linked.
diff --git a/ui/include/classes/core/ZBase.php b/ui/include/classes/core/ZBase.php
index 9c8ea589fb6..21e406536b7 100644
--- a/ui/include/classes/core/ZBase.php
+++ b/ui/include/classes/core/ZBase.php
@@ -134,7 +134,6 @@ class ZBase {
require_once 'include/func.inc.php';
require_once 'include/html.inc.php';
require_once 'include/perm.inc.php';
- require_once 'include/audit.inc.php';
require_once 'include/js.inc.php';
require_once 'include/users.inc.php';
require_once 'include/validate.inc.php';
@@ -295,6 +294,7 @@ class ZBase {
$this->rootDir.'/include/classes/api',
$this->rootDir.'/include/classes/api/services',
$this->rootDir.'/include/classes/api/helpers',
+ $this->rootDir.'/include/classes/api/item_types',
$this->rootDir.'/include/classes/api/managers',
$this->rootDir.'/include/classes/api/clients',
$this->rootDir.'/include/classes/api/wrappers',
diff --git a/ui/include/classes/import/CConfigurationImport.php b/ui/include/classes/import/CConfigurationImport.php
index 9697ea9236c..d16635b0a85 100644
--- a/ui/include/classes/import/CConfigurationImport.php
+++ b/ui/include/classes/import/CConfigurationImport.php
@@ -758,8 +758,19 @@ class CConfigurationImport {
foreach ($order_tree[$host] as $item_key => $level) {
$item = $items[$item_key];
$item['hostid'] = $hostid;
+ unset($item['triggers']);
+
$levels[$level] = true;
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
+
+ if (!in_array($item['type'], $delay_types)) {
+ unset($item['delay']);
+ }
+
if (array_key_exists('interface_ref', $item) && $item['interface_ref']) {
$interfaceid = $this->referencer->findInterfaceidByRef($hostid, $item['interface_ref']);
@@ -771,6 +782,7 @@ class CConfigurationImport {
$item['interfaceid'] = $interfaceid;
}
+ unset($item['interface_ref']);
if (array_key_exists('valuemap', $item) && $item['valuemap']) {
$valuemapid = $this->referencer->findValuemapidByName($hostid, $item['valuemap']['name']);
@@ -785,8 +797,8 @@ class CConfigurationImport {
}
$item['valuemapid'] = $valuemapid;
- unset($item['valuemap']);
}
+ unset($item['valuemap']);
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
if (!array_key_exists('key', $item[$master_item_key])) {
@@ -837,17 +849,9 @@ class CConfigurationImport {
if ($itemid !== null) {
$item['itemid'] = $itemid;
- if (!array_key_exists($level, $items_to_update)) {
- $items_to_update[$level] = [];
- }
-
$items_to_update[$level][] = $item;
}
else {
- if (!array_key_exists($level, $items_to_create)) {
- $items_to_create[$level] = [];
- }
-
$items_to_create[$level][] = $item;
}
}
@@ -908,7 +912,7 @@ class CConfigurationImport {
* Update CItem or CItemPrototype with dependency.
*
* @param array $items_by_level Associative array of entities where key is entity dependency
- * level and value is array of entities for this level.
+ * level and value is array of entities for this level.
* @param string $master_item_key Master entity array key in xml parsed data.
* @param CItem|CItemPrototype $api_service Entity service which is capable to proceed with entity update.
*
@@ -917,6 +921,8 @@ class CConfigurationImport {
protected function updateItemsWithDependency(array $items_by_level, string $master_item_key,
CItemGeneral $api_service): void {
foreach ($items_by_level as $items_to_update) {
+ $hostids = [];
+
foreach ($items_to_update as &$item) {
if (array_key_exists($master_item_key, $item)) {
$item['master_itemid'] = $this->referencer->findItemidByKey($item['hostid'],
@@ -930,15 +936,19 @@ class CConfigurationImport {
}
unset($item[$master_item_key]);
}
+
+ unset($item['uuid']);
+
+ $hostids[] = $item['hostid'];
+ unset($item['hostid']);
}
unset($item);
- $updated_items = $api_service->update(array_map(function($item) {
- unset($item['uuid']);
- return $item;
- }, $items_to_update));
+ $updated_items = $api_service->update($items_to_update);
foreach ($items_to_update as $index => $item) {
+ $item['hostid'] = array_shift($hostids);
+
$this->referencer->setDbItem($updated_items['itemids'][$index], $item);
}
}
@@ -1138,8 +1148,19 @@ class CConfigurationImport {
foreach ($item_prototypes as $index => $level) {
$item_prototype = $discovery_rule['item_prototypes'][$index];
$item_prototype['hostid'] = $hostid;
+ unset($item_prototype['trigger_prototypes']);
+
$levels[$level] = true;
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
+
+ if (!in_array($item_prototype['type'], $delay_types)) {
+ unset($item_prototype['delay']);
+ }
+
if (array_key_exists('interface_ref', $item_prototype) && $item_prototype['interface_ref']) {
$interfaceid = $this->referencer->findInterfaceidByRef($hostid,
$item_prototype['interface_ref']
@@ -1158,6 +1179,7 @@ class CConfigurationImport {
));
}
}
+ unset($item_prototype['interface_ref']);
if ($item_prototype['valuemap']) {
$valuemapid = $this->referencer->findValuemapidByName($hostid,
@@ -1175,8 +1197,8 @@ class CConfigurationImport {
}
$item_prototype['valuemapid'] = $valuemapid;
- unset($item_prototype['valuemap']);
}
+ unset($item_prototype['valuemap']);
if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
if (!array_key_exists('key', $item_prototype[$master_item_key])) {
@@ -1220,11 +1242,9 @@ class CConfigurationImport {
? $this->referencer->findItemidByUuid($item_prototype['uuid'])
: $this->referencer->findItemidByKey($hostid, $item_prototype['key_']);
- $item_prototype['rule'] = [
- 'hostid' => $hostid,
- 'key' => $discovery_rule['key_']
- ];
- $item_prototype['ruleid'] = $itemid;
+ if ($item_prototypeid === null) {
+ $item_prototype['ruleid'] = $itemid;
+ }
foreach ($item_prototype['preprocessing'] as &$preprocessing_step) {
$preprocessing_step['params'] = implode("\n", $preprocessing_step['parameters']);
@@ -2701,23 +2721,17 @@ class CConfigurationImport {
$parent_item_keys = [];
$resolved_masters_cache = [];
- $host_name_to_hostid = array_fill_keys(array_keys($items_by_hosts), null);
+ $host_name_to_hostid = [];
- foreach ($host_name_to_hostid as $host_name => &$hostid) {
+ foreach ($items_by_hosts as $host_name => $items) {
$hostid = $this->referencer->findTemplateidOrHostidByHost($host_name);
- }
- unset($hostid);
- foreach ($items_by_hosts as $host_name => $items) {
- if (!array_key_exists($host_name, $host_name_to_hostid)) {
- throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'host',
- _s('value "%1$s" not found', $host_name)
- ));
+ if ($hostid === null) {
+ unset($items_by_hosts[$host_name]);
+ continue;
}
- if (!array_key_exists($host_name, $resolved_masters_cache)) {
- $resolved_masters_cache[$host_name] = [];
- }
+ $host_name_to_hostid[$host_name] = $hostid;
// Cache input array entities.
foreach ($items as $item) {
@@ -2727,7 +2741,7 @@ class CConfigurationImport {
];
if ($item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('key', $item[$master_item_key])) {
- $parent_item_hostids[$host_name_to_hostid[$host_name]] = true;
+ $parent_item_hostids[$hostid] = true;
$parent_item_keys[$item[$master_item_key]['key']] = true;
}
}
diff --git a/ui/include/classes/import/CImportReferencer.php b/ui/include/classes/import/CImportReferencer.php
index 0ccc0915b92..16017a2e14c 100644
--- a/ui/include/classes/import/CImportReferencer.php
+++ b/ui/include/classes/import/CImportReferencer.php
@@ -1000,7 +1000,7 @@ class CImportReferencer {
'output' => ['name', 'uuid'],
'filter' => [
'uuid' => array_column($this->template_groups, 'uuid'),
- 'name' => array_column($this->template_groups, 'name')
+ 'name' => array_keys($this->template_groups)
],
'searchByAny' => true,
'preservekeys' => true
@@ -1023,7 +1023,7 @@ class CImportReferencer {
'output' => ['name', 'uuid'],
'filter' => [
'uuid' => array_column($this->host_groups, 'uuid'),
- 'name' => array_column($this->host_groups, 'name')
+ 'name' => array_keys($this->host_groups)
],
'searchByAny' => true,
'preservekeys' => true
diff --git a/ui/include/classes/import/converters/C10ImportConverter.php b/ui/include/classes/import/converters/C10ImportConverter.php
index d901a4f62e8..7e4eafe7fad 100644
--- a/ui/include/classes/import/converters/C10ImportConverter.php
+++ b/ui/include/classes/import/converters/C10ImportConverter.php
@@ -323,26 +323,22 @@ class C10ImportConverter extends CConverter {
// map items to new interfaces
if (isset($host['items']) && $host['items']) {
- $item_interface = [
- ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT,
- ITEM_TYPE_SNMPV1 => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_SIMPLE => INTERFACE_TYPE_AGENT,
- ITEM_TYPE_SNMPV2C => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_SNMPV3 => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY,
- ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI,
- ITEM_TYPE_SSH => INTERFACE_TYPE_ANY,
- ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY
- ];
-
foreach ($host['items'] as &$item) {
- if (!array_key_exists('type', $item) || !array_key_exists($item['type'], $item_interface)) {
+ if (!array_key_exists('type', $item)) {
continue;
}
- switch ($item_interface[$item['type']]) {
+ // 1.8 till 4.4 uses the old item types.
+ if (in_array($item['type'], [ITEM_TYPE_SNMPV1, ITEM_TYPE_SNMPV2C, ITEM_TYPE_SNMPV3])) {
+ $interface_type = INTERFACE_TYPE_SNMP;
+ }
+ else {
+ $interface_type = itemTypeInterface($item['type']);
+ }
+
+ switch ($interface_type) {
case INTERFACE_TYPE_AGENT:
- case INTERFACE_TYPE_ANY:
+ case INTERFACE_TYPE_OPT:
$item['interface_ref'] = $agentInterface['interface_ref'];
break;
diff --git a/ui/include/classes/import/converters/C62ImportConverter.php b/ui/include/classes/import/converters/C62ImportConverter.php
index 9d236322a7d..1a79ace08ff 100644
--- a/ui/include/classes/import/converters/C62ImportConverter.php
+++ b/ui/include/classes/import/converters/C62ImportConverter.php
@@ -34,6 +34,86 @@ class C62ImportConverter extends CConverter {
public function convert(array $data): array {
$data['zabbix_export']['version'] = '6.4';
+ if (array_key_exists('hosts', $data['zabbix_export'])) {
+ $data['zabbix_export']['hosts'] = self::convertHosts($data['zabbix_export']['hosts']);
+ }
+
+ if (array_key_exists('templates', $data['zabbix_export'])) {
+ $data['zabbix_export']['templates'] = self::convertTemplates($data['zabbix_export']['templates']);
+ }
+
return $data;
}
+
+ /**
+ * Convert hosts.
+ *
+ * @param array $hosts
+ *
+ * @return array
+ */
+ private static function convertHosts(array $hosts): array {
+ foreach ($hosts as &$host) {
+ if (array_key_exists('discovery_rules', $host)) {
+ $host['discovery_rules'] = self::convertDiscoveryRules($host['discovery_rules']);
+ }
+ }
+ unset($host);
+
+ return $hosts;
+ }
+
+ /**
+ * Convert templates.
+ *
+ * @param array $templates
+ *
+ * @return array
+ */
+ private static function convertTemplates(array $templates): array {
+ foreach ($templates as &$template) {
+ if (array_key_exists('discovery_rules', $template)) {
+ $template['discovery_rules'] = self::convertDiscoveryRules($template['discovery_rules']);
+ }
+ }
+ unset($template);
+
+ return $templates;
+ }
+
+ /**
+ * Convert discovery rules.
+ *
+ * @param array $discovery_rules
+ *
+ * @return array
+ */
+ private static function convertDiscoveryRules(array $discovery_rules): array {
+ foreach ($discovery_rules as &$discovery_rule) {
+ if (array_key_exists('item_prototypes', $discovery_rule)) {
+ $discovery_rule['item_prototypes'] = self::convertItemPrototypes($discovery_rule['item_prototypes']);
+ }
+ }
+ unset($discovery_rule);
+
+ return $discovery_rules;
+ }
+
+ /**
+ * Convert item prototypes.
+ *
+ * @param array $item_prototypes
+ *
+ * @return array
+ */
+ private static function convertItemPrototypes(array $item_prototypes): array {
+ foreach ($item_prototypes as &$item_prototype) {
+ if (array_key_exists('inventory_link', $item_prototype)) {
+ unset($item_prototype['inventory_link']);
+ }
+ }
+ unset($item_prototype);
+
+ return $item_prototypes;
+ }
}
diff --git a/ui/include/classes/import/validators/C64XmlValidator.php b/ui/include/classes/import/validators/C64XmlValidator.php
index 8cb51d81ad6..4b385810d3e 100644
--- a/ui/include/classes/import/validators/C64XmlValidator.php
+++ b/ui/include/classes/import/validators/C64XmlValidator.php
@@ -638,7 +638,6 @@ class C64XmlValidator extends CXmlValidatorGeneral {
'publickey' => ['type' => XML_STRING, 'default' => ''],
'privatekey' => ['type' => XML_STRING, 'default' => ''],
'description' => ['type' => XML_STRING, 'default' => ''],
- 'inventory_link' => ['type' => XML_STRING, 'default' => CXmlConstantValue::NONE, 'in' => $this->ITEM_INVENTORY_LINK],
'valuemap' => ['type' => XML_ARRAY, 'rules' => [
'name' => ['type' => XML_STRING | XML_REQUIRED]
]],
@@ -1324,7 +1323,6 @@ class C64XmlValidator extends CXmlValidatorGeneral {
'publickey' => ['type' => XML_STRING, 'default' => ''],
'privatekey' => ['type' => XML_STRING, 'default' => ''],
'description' => ['type' => XML_STRING, 'default' => ''],
- 'inventory_link' => ['type' => XML_STRING, 'default' => CXmlConstantValue::NONE, 'in' => $this->ITEM_INVENTORY_LINK],
'valuemap' => ['type' => XML_ARRAY, 'rules' => [
'name' => ['type' => XML_STRING | XML_REQUIRED]
]],
diff --git a/ui/include/classes/parsers/CPrometheusOutputParser.php b/ui/include/classes/parsers/CPrometheusOutputParser.php
index fc27f69fccb..3e39974518a 100644
--- a/ui/include/classes/parsers/CPrometheusOutputParser.php
+++ b/ui/include/classes/parsers/CPrometheusOutputParser.php
@@ -30,6 +30,8 @@ class CPrometheusOutputParser extends CParser {
];
private $user_macro_parser;
+ private $lld_macro_parser;
+ private $lld_macro_function_parser;
public function __construct($options = []) {
if (array_key_exists('usermacros', $options)) {
@@ -44,6 +46,7 @@ class CPrometheusOutputParser extends CParser {
}
if ($this->options['lldmacros']) {
$this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
}
}
@@ -94,6 +97,12 @@ class CPrometheusOutputParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
diff --git a/ui/include/classes/parsers/CPrometheusPatternParser.php b/ui/include/classes/parsers/CPrometheusPatternParser.php
index 6cfde3c3939..d45a50e7589 100644
--- a/ui/include/classes/parsers/CPrometheusPatternParser.php
+++ b/ui/include/classes/parsers/CPrometheusPatternParser.php
@@ -31,6 +31,7 @@ class CPrometheusPatternParser extends CParser {
private $user_macro_parser;
private $lld_macro_parser;
+ private $lld_macro_function_parser;
public function __construct($options = []) {
if (array_key_exists('usermacros', $options)) {
@@ -45,6 +46,7 @@ class CPrometheusPatternParser extends CParser {
}
if ($this->options['lldmacros']) {
$this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
}
}
@@ -138,6 +140,12 @@ class CPrometheusPatternParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
@@ -176,6 +184,10 @@ class CPrometheusPatternParser extends CParser {
elseif ($this->options['lldmacros'] && $this->lld_macro_parser->parse($source, $pos) != self::PARSE_FAIL) {
$p += $this->lld_macro_parser->getLength();
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $p += $this->lld_macro_function_parser->getLength();
+ }
else {
return false;
}
@@ -321,6 +333,12 @@ class CPrometheusPatternParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
diff --git a/ui/include/classes/parsers/CUpdateIntervalParser.php b/ui/include/classes/parsers/CUpdateIntervalParser.php
index 5ff772d94e9..0fbb2b49c80 100644
--- a/ui/include/classes/parsers/CUpdateIntervalParser.php
+++ b/ui/include/classes/parsers/CUpdateIntervalParser.php
@@ -76,6 +76,8 @@ class CUpdateIntervalParser extends CParser {
// First interval must be simple interval (or macro). Other intervals may be mixed and repeat multiple times.
if ($this->simple_interval_parser->parse($source, $p) == self::PARSE_FAIL) {
+ $this->errorPos($source, $p);
+
return self::PARSE_FAIL;
}
$p += $this->simple_interval_parser->getLength();
@@ -111,7 +113,13 @@ class CUpdateIntervalParser extends CParser {
$this->length = $p - $pos;
$this->match = substr($source, $pos, $this->length);
- return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ if (!isset($source[$p])) {
+ return self::PARSE_SUCCESS;
+ }
+
+ $this->errorPos($source, $p);
+
+ return self::PARSE_SUCCESS_CONT;
}
/**
@@ -124,26 +132,25 @@ class CUpdateIntervalParser extends CParser {
}
/**
- * Get all intervals or specifically flexible or scheduling intervals.
+ * Get all intervals, or specifically flexible or scheduling ones.
*
- * @param int $type If null get both types, else either ITEM_DELAY_FLEXIBLE or ITEM_DELAY_SCHEDULING
+ * @param int|null $type ITEM_DELAY_FLEXIBLE, ITEM_DELAY_SCHEDULING, or null for both.
*
* @return array
*/
- public function getIntervals($type = null) {
+ public function getIntervals(?int $type = null) {
if ($type === null) {
return $this->intervals;
}
- else {
- $intervals = [];
- foreach ($this->intervals as $interval) {
- if ($interval['type'] == $type) {
- $intervals[] = $interval['interval'];
- }
- }
+ $intervals = [];
- return $intervals;
+ foreach ($this->intervals as $interval) {
+ if ($interval['type'] == $type) {
+ $intervals[] = $interval['interval'];
+ }
}
+
+ return $intervals;
}
}
diff --git a/ui/include/classes/validators/CApiInputValidator.php b/ui/include/classes/validators/CApiInputValidator.php
index 971e3f570a7..106677afd20 100644
--- a/ui/include/classes/validators/CApiInputValidator.php
+++ b/ui/include/classes/validators/CApiInputValidator.php
@@ -242,6 +242,30 @@ class CApiInputValidator {
case API_TG_NAME:
return self::validateTemplateGroupName($rule, $data, $path, $error);
+
+ case API_ANY:
+ return true;
+
+ case API_ITEM_KEY:
+ return self::validateItemKey($rule, $data, $path, $error);
+
+ case API_ITEM_DELAY:
+ return self::validateItemDelay($rule, $data, $path, $error);
+
+ case API_JSON:
+ return self::validateJson($rule, $data, $path, $error);
+
+ case API_XML:
+ return self::validateXml($rule, $data, $path, $error);
+
+ case API_PREPROC_PARAMS:
+ return self::validatePreprocParams($rule, $data, $path, $error);
+
+ case API_PROMETHEUS_PATTERN:
+ return self::validatePrometheusPattern($rule, $data, $path, $error);
+
+ case API_PROMETHEUS_LABEL:
+ return self::validatePrometheusLabel($rule, $data, $path, $error);
}
// This message can be untranslated because warn about incorrect validation rules at a development stage.
@@ -314,6 +338,14 @@ class CApiInputValidator {
case API_LAT_LNG_ZOOM:
case API_TIMESTAMP:
case API_TG_NAME:
+ case API_ANY:
+ case API_ITEM_KEY:
+ case API_ITEM_DELAY:
+ case API_JSON:
+ case API_XML:
+ case API_PREPROC_PARAMS:
+ case API_PROMETHEUS_PATTERN:
+ case API_PROMETHEUS_LABEL:
return true;
case API_OBJECT:
@@ -924,7 +956,7 @@ class CApiInputValidator {
* Floating point number validator.
*
* @param array $rule
- * @param int $rule['flags'] (optional) API_ALLOW_NULL
+ * @param int $rule['flags'] (optional) API_ALLOW_NULL | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
* @param string $rule['in'] (optional) a comma-delimited character string, for example: '0,60:900'
* @param mixed $data
* @param string $path
@@ -932,7 +964,7 @@ class CApiInputValidator {
*
* @return bool
*/
- private static function validateFloat($rule, &$data, $path, &$error) {
+ private static function validateFloat(array $rule, &$data, string $path, string &$error): bool {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
if (($flags & API_ALLOW_NULL) && $data === null) {
@@ -949,6 +981,21 @@ class CApiInputValidator {
$value = (float) $number_parser->getMatch();
}
else {
+ if ($flags & API_ALLOW_USER_MACRO) {
+ $user_macro_parser = new CUserMacroParser();
+ }
+
+ if ($flags & API_ALLOW_LLD_MACRO) {
+ $lld_macro_parser = new CLLDMacroParser();
+ $lld_macro_function_parser = new CLLDMacroFunctionParser();
+ }
+
+ if (($flags & API_ALLOW_USER_MACRO && $user_macro_parser->parse($data) == CParser::PARSE_SUCCESS)
+ || ($flags & API_ALLOW_LLD_MACRO && ($lld_macro_parser->parse($data) == CParser::PARSE_SUCCESS
+ || $lld_macro_function_parser->parse($data) == CParser::PARSE_SUCCESS))) {
+ return true;
+ }
+
$value = NAN;
}
}
@@ -966,6 +1013,10 @@ class CApiInputValidator {
return false;
}
+ if (!self::checkCompare($rule, $value, $path, $error)) {
+ return false;
+ }
+
$data = $value;
return true;
@@ -989,6 +1040,8 @@ class CApiInputValidator {
return true;
}
+ $e = '';
+
if (($flags & API_NORMALIZE) && self::validateFloat([], $data, '', $e)) {
$data = [$data];
}
@@ -1023,7 +1076,7 @@ class CApiInputValidator {
foreach (explode(',', $in) as $in) {
if (strpos($in, ':') !== false) {
- list($from, $to) = explode(':', $in);
+ [$from, $to] = explode(':', $in);
}
else {
$from = $in;
@@ -1073,7 +1126,7 @@ class CApiInputValidator {
*
* @return bool
*/
- private static function checkFloatIn($rule, $data, $path, &$error) {
+ private static function checkFloatIn(array $rule, $data, string $path, string &$error) {
if (!array_key_exists('in', $rule)) {
return true;
}
@@ -1144,12 +1197,12 @@ class CApiInputValidator {
/**
* Validate integer ranges.
* Example:
- * 0-100,200,300-400
+ * -100-0,0-100,200,300-{$MACRO},{$MACRO},{#LLD},400-500
*
* @static
*
* @param array $rule
- * @param int $rule['flags'] (optional) API_NOT_EMPTY
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
* @param int $rule['length'] (optional)
* @param string $rule['in'] (optional) A comma-delimited character string, for example: '0,60:900'
* @param mixed $data
@@ -1161,7 +1214,7 @@ class CApiInputValidator {
private static function validateInt32Ranges(array $rule, &$data, string $path, string &$error): bool {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
- if (self::checkStringUtf8($flags, $data, $path, $error) === false) {
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
return false;
}
@@ -1174,7 +1227,11 @@ class CApiInputValidator {
return false;
}
- $parser = new CRangesParser(['with_minus' => true]);
+ $parser = new CRangesParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO),
+ 'with_minus' => true
+ ]);
if ($parser->parse($data) != CParser::PARSE_SUCCESS) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid range expression'));
@@ -1183,6 +1240,10 @@ class CApiInputValidator {
foreach ($parser->getRanges() as $ranges) {
foreach ($ranges as $range) {
+ if (($flags & (API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO)) && $range[0] === '{') {
+ continue;
+ }
+
if (!self::checkInt32In($rule, $range, $path, $error)) {
return false;
}
@@ -1196,13 +1257,14 @@ class CApiInputValidator {
* Identifier validator.
*
* @param array $rule
+ * @param string $rule['in'] (optional)
* @param mixed $data
* @param string $path
* @param string $error
*
* @return bool
*/
- private static function validateId($rule, &$data, $path, &$error) {
+ private static function validateId(array $rule, &$data, string $path, string &$error) :bool {
if (!is_scalar($data) || is_bool($data) || is_double($data) || !ctype_digit(strval($data))) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is expected'));
return false;
@@ -1223,6 +1285,39 @@ class CApiInputValidator {
}
}
+ if (!self::checkIdIn($rule, $data, $path, $error)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param string $rule['in'] (optional)
+ * @param string $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function checkIdIn(array $rule, string $data, string $path, string &$error): bool {
+ if (!array_key_exists('in', $rule)) {
+ return true;
+ }
+
+ if ($rule['in'] != 0) {
+ $error = 'Incorrect validation rules.';
+
+ return false;
+ }
+
+ if ($data != 0) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _s('value must be %1$s', '0'));
+
+ return false;
+ }
+
return true;
}
@@ -1392,6 +1487,10 @@ class CApiInputValidator {
$field_rule['compare']['value'] = $data[$field_rule['compare']['field']];
}
+ if (array_key_exists('preproc_type', $field_rule)) {
+ $field_rule['preproc_type']['value'] = $data[$field_rule['preproc_type']['field']];
+ }
+
if (array_key_exists($field_name, $data)) {
$subpath = ($path === '/' ? $path : $path.'/').$field_name;
if (!self::validateData($field_rule, $data[$field_name], $subpath, $error)) {
@@ -1554,6 +1653,8 @@ class CApiInputValidator {
return true;
}
+ $e = '';
+
if (($flags & API_NORMALIZE) && self::validateId([], $data, '', $e)) {
$data = [$data];
}
@@ -2023,7 +2124,7 @@ class CApiInputValidator {
return true;
}
- if (@preg_match('/'.str_replace('/', '\/', $data).'/', '') === false) {
+ if (@preg_match('('.$data.')', '') === false) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid regular expression'));
return false;
}
@@ -2098,7 +2199,7 @@ class CApiInputValidator {
/**
* Array of ids, int32 or strings uniqueness validator.
*
- * @param bool $rule
+ * @param array $rule
* @param integer $rule['type']
* @param bool $rule['uniq'] (optional)
* @param array|null $data
@@ -2192,7 +2293,7 @@ class CApiInputValidator {
foreach ($data as $index => $object) {
$_uniq = &$uniq;
- $values = [];
+ $object_values = [];
$level = 1;
foreach ($field_names as $field_name) {
@@ -2200,29 +2301,104 @@ class CApiInputValidator {
break;
}
- $values[] = $object[$field_name];
+ $object_values[] = $object[$field_name];
- $value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
+ $object_value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
? self::trimMacro($object[$field_name])
: $object[$field_name];
if ($level < count($field_names)) {
- if (!array_key_exists($value, $_uniq)) {
- $_uniq[$value] = [];
+ if (!array_key_exists($object_value, $_uniq)) {
+ $_uniq[$object_value] = [];
}
- $_uniq = &$_uniq[$value];
+ $_uniq = &$_uniq[$object_value];
}
else {
- if (array_key_exists($value, $_uniq)) {
+ if (array_key_exists($object_value, $_uniq)) {
$subpath = ($path === '/' ? $path : $path.'/').($index + 1);
$error = _s('Invalid parameter "%1$s": %2$s.', $subpath, _s('value %1$s already exists',
- '('.implode(', ', $field_names).')=('.implode(', ', $values).')'
+ '('.implode(', ', $field_names).')=('.implode(', ', $object_values).')'
));
return false;
}
- $_uniq[$value] = true;
+ $_uniq[$object_value] = true;
+ }
+
+ $level++;
+ }
+ }
+ }
+ }
+
+ if (array_key_exists('uniq_by_values', $rule)) {
+ foreach ($rule['uniq_by_values'] as $field_values) {
+ $uniq = [];
+ $_uniqs = [&$uniq];
+
+ foreach ($data as $index => $object) {
+ $object_values = [];
+ $level = 1;
+
+ foreach ($field_values as $field_name => $values) {
+ if (!array_key_exists($field_name, $object)) {
+ $_uniqs = [&$uniq];
+ break;
+ }
+
+ $object_values[] = $object[$field_name];
+
+ $object_value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
+ ? self::trimMacro($object[$field_name])
+ : $object[$field_name];
+
+ if (!in_array($object_value, $values)) {
+ $_uniqs = [&$uniq];
+ break;
+ }
+
+ if ($level < count($field_values)) {
+ $__uniqs = [];
+
+ foreach ($_uniqs as &$_uniq) {
+ foreach ($values as $value) {
+ if (!array_key_exists($value, $_uniq)) {
+ $_uniq[$value] = [];
+ }
+
+ $__uniqs[] = &$_uniq[$value];
+ }
+ }
+ unset($_uniq);
+
+ $_uniqs = $__uniqs;
+ }
+ else {
+ foreach ($_uniqs as &$_uniq) {
+ foreach ($values as $value) {
+ if (array_key_exists($value, $_uniq)) {
+ $subpath = ($path === '/' ? $path : $path.'/').($index + 1);
+
+ $combinations = array_map(static function (array $values): string {
+ return '('.implode(', ', $values).')';
+ }, $field_values);
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $subpath,
+ _s('only one object can exist within the combinations of %1$s',
+ '('.implode(', ', array_keys($field_values)).')=('.
+ implode(', ', $combinations).')'
+ )
+ );
+ return false;
+ }
+
+ $_uniq[$value] = true;
+ }
+ }
+ unset($_uniq);
+
+ $_uniqs = [&$uniq];
}
$level++;
@@ -2445,21 +2621,22 @@ class CApiInputValidator {
/**
* Validate IP ranges. Multiple IPs separated by comma character.
* Example:
- * 127.0.0.1,192.168.1.1-254,192.168.2.1-100,192.168.3.0/24
+ * 127.0.0.1,192.168.1.1-254,192.168.2.1-100,192.168.3.0/24,{$MACRO}
*
- * @param array $rule
- * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_DNS, API_ALLOW_RANGE
- * @param int $rule['length'] (optional)
- * @param mixed $data
- * @param string $path
- * @param string $error
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_DNS, API_ALLOW_RANGE, API_ALLOW_USER_MACRO
+ * @param array|bool $rule['macros'] (optional) An array of supported macros. True - all macros are supported.
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
*
* @return bool
*/
private static function validateIpRanges($rule, &$data, $path, &$error) {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
- if (self::checkStringUtf8($flags, $data, $path, $error) === false) {
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
return false;
}
@@ -2474,8 +2651,10 @@ class CApiInputValidator {
$ip_range_parser = new CIPRangeParser([
'v6' => ZBX_HAVE_IPV6,
- 'dns' => ($flags & API_ALLOW_DNS),
- 'ranges' => ($flags & API_ALLOW_RANGE)
+ 'dns' => (bool) ($flags & API_ALLOW_DNS),
+ 'ranges' => (bool) ($flags & API_ALLOW_RANGE),
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'macros' => array_key_exists('macros', $rule) ? $rule['macros'] : []
]);
if (!$ip_range_parser->parse($data)) {
@@ -3186,10 +3365,10 @@ class CApiInputValidator {
/**
* @param array $rule
- * @param array $rule[compare] (optional)
- * @param string $rule[compare][operator]
- * @param string $rule[compare][path]
- * @param mixed $rule[compare][value]
+ * @param array $rule['compare'] (optional)
+ * @param string $rule['compare']['operator']
+ * @param string $rule['compare']['path']
+ * @param mixed $rule['compare']['value']
* @param int $data
* @param string $path
* @param string $error
@@ -3197,7 +3376,7 @@ class CApiInputValidator {
* @return bool
*/
private static function checkCompare(array $rule, $data, string $path, string &$error): bool {
- if (!array_key_exists('compare', $rule)) {
+ if (!array_key_exists('compare', $rule) || $rule['compare']['value'] === null) {
return true;
}
@@ -3226,7 +3405,7 @@ class CApiInputValidator {
*
* @param string $field_name
* @param array $field_rule
- * @param string $field_rule[error_type] (optional) API_ERR_INHERITED, API_ERR_DISCOVERED
+ * @param string $field_rule['error_type'] (optional) API_ERR_INHERITED, API_ERR_DISCOVERED
* @param array $object
* @param string $path
* @param string $error
@@ -3264,4 +3443,719 @@ class CApiInputValidator {
return false;
}
+
+ /**
+ * @param array $rule
+ * @param int $rule['length'] (optional)
+ * @param int $rule['flags'] (optional) API_REQUIRED_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateItemKey(array $rule, &$data, string $path, string &$error): bool {
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ 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;
+ }
+
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+ $item_key_parser = new CItemKey();
+
+ if ($item_key_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $item_key_parser->getError());
+ return false;
+ }
+
+ if (($flags & API_REQUIRED_LLD_MACRO)) {
+ $parameters = $item_key_parser->getParamsRaw();
+ $lld_macro_parser = new CLLDMacroParser();
+ $lld_macro_function_parser = new CLLDMacroFunctionParser();
+ $has_lld_macros = false;
+
+ if ($parameters) {
+ $parameters = $parameters[0]['raw'];
+ $p = 1;
+
+ while (isset($parameters[$p])) {
+ if ($lld_macro_parser->parse($parameters, $p) != CParser::PARSE_FAIL
+ || $lld_macro_function_parser->parse($parameters, $p) != CParser::PARSE_FAIL) {
+ $has_lld_macros = true;
+ break;
+ }
+
+ $p++;
+ }
+ }
+
+ if (!$has_lld_macros) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('must contain at least one low-level discovery macro')
+ );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Try to parse delay/interval information and check that some polling can be performed during the schedule-week.
+ *
+ * Note: In case of non-convertible entries (containing macros), we can only check for edge cases, e.g.
+ * where the whole week is fully blocked by periods with an update interval of 0.
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateItemDelay(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (is_int($data)) {
+ $data = (string) $data;
+ }
+
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ 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;
+ }
+
+ $update_interval_parser = new CUpdateIntervalParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($update_interval_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = strpos($data, ';') === false
+ ? _s('Invalid parameter "%1$s": %2$s.', $path, _('a time unit is expected'))
+ : _s('Invalid parameter "%1$s": %2$s.', $path, $update_interval_parser->getError());
+
+ return false;
+ }
+
+ $delay = $update_interval_parser->getDelay();
+ $intervals = $update_interval_parser->getIntervals();
+
+ if ($delay[0] !== '{') {
+ $delay_sec = timeUnitToSeconds($delay);
+
+ if ($delay_sec == 0 && !$intervals) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('cannot be equal to zero without custom intervals')
+ );
+
+ return false;
+ }
+
+ if ($delay_sec > SEC_PER_DAY) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _s('value must be one of %1$s', implode('-', [0, SEC_PER_DAY]))
+ );
+
+ return false;
+ }
+ }
+
+ if (!$intervals || array_key_exists(ITEM_DELAY_SCHEDULING, array_column($intervals, null, 'type'))) {
+ return true;
+ }
+
+ $active_macro_interval = false;
+
+ foreach ($intervals as $i => $interval) {
+ if (strpos($interval['interval'], '{') !== false) {
+ unset($intervals[$i]);
+
+ if (strpos($interval['update_interval'], '{') === false) {
+ if ($interval['update_interval'] != 0) {
+ $active_macro_interval = true;
+ }
+ }
+ else {
+ $active_macro_interval = true;
+ }
+ }
+ }
+
+ $inactive_intervals = [];
+ $active_intervals = [];
+
+ foreach ($intervals as $interval) {
+ $update_interval = timeUnitToSeconds($interval['update_interval']);
+
+ [$day_period, $time_period] = explode(',', $interval['time_period']);
+
+ [$day_from, $day_to] = (strpos($day_period, '-') === false)
+ ? [$day_period, $day_period]
+ : explode('-', $day_period);
+
+ [$time_from, $time_to] = explode('-', $time_period);
+
+ [$time_from_hours, $time_from_minutes] = explode(':', $time_from);
+ [$time_to_hours, $time_to_minutes] = explode(':', $time_to);
+
+ $time_from = $time_from_hours * SEC_PER_HOUR + $time_from_minutes * SEC_PER_MIN;
+ $time_to = $time_to_hours * SEC_PER_HOUR + $time_to_minutes * SEC_PER_MIN;
+
+ if ($update_interval > 0) {
+ if ($time_from == 0 && $time_to == SEC_PER_DAY && $day_to - $day_from > 0) {
+ $_interval = $day_to * SEC_PER_DAY + $time_to - $day_from * SEC_PER_DAY + $time_from;
+ }
+ else {
+ $_interval = $time_to - $time_from;
+ }
+
+ if ($update_interval > $_interval) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _s('update interval "%1$s" is longer than period "%2$s"', $interval['update_interval'],
+ $interval['time_period']
+ )
+ );
+
+ return false;
+ }
+ }
+
+ for ($day = $day_from; $day <= $day_to; $day++) {
+ if ($update_interval == 0) {
+ $inactive_intervals[] = [
+ 'time_from' => ($day - 1) * SEC_PER_DAY + $time_from,
+ 'time_to' => ($day - 1) * SEC_PER_DAY + $time_to
+ ];
+ }
+ else {
+ $active_intervals[] = [
+ 'update_interval' => $update_interval,
+ 'time_from' => ($day - 1) * SEC_PER_DAY + $time_from,
+ 'time_to' => ($day - 1) * SEC_PER_DAY + $time_to
+ ];
+ }
+ }
+ }
+
+ if ($delay[0] !== '{' && $delay_sec == 0 && !$active_intervals && !$active_macro_interval) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('must have at least one interval greater than 0'));
+
+ return false;
+ }
+
+ CArrayHelper::sort($inactive_intervals, ['time_from']);
+
+ $_inactive_intervals = $inactive_intervals ? [array_shift($inactive_intervals)] : [];
+ $last = 0;
+
+ foreach ($inactive_intervals as $interval) {
+ if ($interval['time_from'] > $_inactive_intervals[$last]['time_to']) {
+ $_inactive_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['time_to'] <= $_inactive_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ $_inactive_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+
+ $inactive_intervals = $_inactive_intervals;
+
+ if ($inactive_intervals && $inactive_intervals[0]['time_from'] == 0
+ && $inactive_intervals[0]['time_to'] == 7 * SEC_PER_DAY) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('non-active intervals cannot fill the entire time')
+ );
+
+ return false;
+ }
+
+ if ($delay[0] === '{' || $active_macro_interval) {
+ return true;
+ }
+
+ CArrayHelper::sort($active_intervals, ['time_from']);
+
+ $_active_intervals = $active_intervals ? [array_shift($active_intervals)] : [];
+ $last = 0;
+
+ foreach ($active_intervals as $i => $interval) {
+ if ($interval['time_from'] > $_active_intervals[$last]['time_to']) {
+ $_active_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['update_interval'] >= $_active_intervals[$last]['update_interval']) {
+ if ($interval['time_to'] <= $_active_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ if ($interval['update_interval'] == $_active_intervals[$last]['update_interval']) {
+ $_active_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+ else {
+ ++$last;
+ $_active_intervals[$last] = ['time_from' => $_active_intervals[$last - 1]['time_to']] + $interval;
+ }
+ }
+ else {
+ $_active_interval = $_active_intervals[$last];
+
+ if ($_active_intervals[$last]['time_from'] == $interval['time_from']) {
+ $_active_intervals[$last] = $interval;
+ }
+ else {
+ $_active_intervals[$last]['time_to'] = $interval['time_from'];
+ $_active_intervals[++$last] = $interval;
+ }
+
+ if ($_active_interval['time_to'] > $interval['time_to']) {
+ $_active_intervals[++$last] = ['time_from' => $interval['time_to']] + $_active_interval;
+ }
+ }
+ }
+
+ $active_intervals = $_active_intervals;
+
+ foreach ($active_intervals as $active_interval) {
+ if ($active_interval['time_to'] - $active_interval['time_from'] < $active_interval['update_interval']) {
+ continue;
+ }
+
+ if (!$inactive_intervals) {
+ return true;
+ }
+
+ $_inactive_intervals = [];
+
+ foreach ($inactive_intervals as $inactive_interval) {
+ if ($inactive_interval['time_from'] < $active_interval['time_to']
+ && $inactive_interval['time_to'] > $active_interval['time_from']) {
+ $_inactive_intervals[] = $inactive_interval;
+ }
+ }
+
+ if (!$_inactive_intervals) {
+ return true;
+ }
+
+ foreach ($_inactive_intervals as $i => $inactive_interval) {
+ if ($i == 0 && $inactive_interval['time_from'] > $active_interval['time_from']) {
+ $active_time_from = $active_interval['time_from'];
+ $active_time_to = $inactive_interval['time_from'];
+
+ if ($active_time_to - $active_time_from >= $active_interval['update_interval']) {
+ return true;
+ }
+ }
+
+ $active_time_from = $inactive_interval['time_to'];
+
+ $active_time_to = array_key_exists($i + 1, $_inactive_intervals)
+ ? $_inactive_intervals[$i + 1]['time_from']
+ : $active_interval['time_to'];
+
+ if ($active_time_to - $active_time_from >= $active_interval['update_interval']) {
+ return true;
+ }
+ }
+ }
+
+ if ($delay_sec > 0) {
+ $intervals = array_merge($inactive_intervals, $active_intervals);
+ CArrayHelper::sort($intervals, ['time_from']);
+
+ $_intervals = $intervals ? [array_shift($intervals)] : [];
+ $last = 0;
+
+ foreach ($intervals as $interval) {
+ if ($interval['time_from'] > $_intervals[$last]['time_to']) {
+ $_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['time_to'] <= $_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ $_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+
+ foreach ($_intervals as $i => $interval) {
+ if ($i == 0) {
+ if ($interval['time_from'] > 0 && $interval['time_from'] >= $delay_sec) {
+ return true;
+ }
+
+ continue;
+ }
+
+ if ($interval['time_from'] - $_intervals[$i - 1]['time_to'] >= $delay_sec) {
+ return true;
+ }
+ }
+
+ if (7 * SEC_PER_DAY - $interval['time_to'] >= $delay_sec) {
+ return true;
+ }
+ }
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('must have a polling interval not blocked by non-active interval periods')
+ );
+
+ return false;
+ }
+
+ /**
+ * JSON validator.
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param array $rule['macros_n'] (optional) An array of supported macros. Example: ['{HOST.IP}', '{ITEM.KEY}'].
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateJson($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 ($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;
+ }
+
+ $json = $data;
+
+ $types = [];
+
+ if ($flags & API_ALLOW_USER_MACRO) {
+ $types['usermacros'] = true;
+ }
+
+ if ($flags & API_ALLOW_LLD_MACRO) {
+ $types['lldmacros'] = true;
+ }
+
+ if (array_key_exists('macros_n', $rule)) {
+ $types['macros_n'] = $rule['macros_n'];
+ }
+
+ if ($types) {
+ $matches = (new CMacrosResolverGeneral())->getMacroPositions($json, $types);
+ $shift = 0;
+
+ foreach ($matches as $pos => $substr) {
+ $json = substr_replace($json, '1', $pos + $shift, strlen($substr));
+ $shift = $shift + 1 - strlen($substr);
+ }
+ }
+
+ json_decode($json);
+
+ if (json_last_error() != JSON_ERROR_NONE) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('JSON is expected'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * XML validator.
+ *
+ * @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 validateXml(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if ($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;
+ }
+
+ libxml_use_internal_errors(true);
+
+ if (simplexml_load_string($data, null, LIBXML_IMPORT_FLAGS) === false) {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+
+ if ($errors) {
+ $error = reset($errors);
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _s('%1$s [Line: %2$s | Column: %3$s]',
+ '('.$error->code.') '.trim($error->message), $error->line, $error->column
+ ));
+ return false;
+ }
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('XML is expected'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param array $rule['preproc_type']
+ * @param int $rule['preproc_type']['value']
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePreprocParams(array $rule, &$data, string $path, string &$error): bool {
+ $preproc_type = $rule['preproc_type']['value'];
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8(0x00, $data, $path, $error) === false) {
+ return false;
+ }
+
+ $data = str_replace("\r\n", "\n", $data);
+
+ 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;
+ }
+
+ $params = [];
+
+ if ($preproc_type == ZBX_PREPROC_SCRIPT) {
+ $params[1] = $data;
+ }
+ else {
+ foreach (explode("\n", $data) as $i => $param) {
+ $params[$i + 1] = $param;
+ }
+ }
+
+ switch ($preproc_type) {
+ case ZBX_PREPROC_MULTIPLIER:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)]
+ ]];
+ break;
+
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_REGSUB:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_XPATH:
+ case ZBX_PREPROC_JSONPATH:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_VALIDATE_RANGE:
+ if (count($params) == 2 && ($params[1] === '' || $params[2] === '')) {
+ if ($params[1] === '' && $params[2] === '') {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty'));
+
+ return false;
+ }
+
+ $params[1] = $params[1] === '' ? null : $params[1];
+ $params[2] = $params[2] === '' ? null : $params[2];
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | API_ALLOW_NULL | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ '2' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | API_ALLOW_NULL | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO), 'compare' => ['operator' => '>', 'field' => '1']]
+ ]];
+ break;
+
+ case ZBX_PREPROC_VALIDATE_REGEX:
+ case ZBX_PREPROC_VALIDATE_NOT_REGEX:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_ERROR_FIELD_JSON:
+ case ZBX_PREPROC_ERROR_FIELD_XML:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_ERROR_FIELD_REGEX:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO), 'in' => implode(':', [1, 25 * SEC_PER_YEAR])]
+ ]];
+ break;
+
+ case ZBX_PREPROC_SCRIPT:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_REQUIRED | API_NOT_EMPTY | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL, ZBX_PREPROC_PROMETHEUS_FUNCTION])],
+ '3' => ['type' => API_MULTIPLE, 'rules' =>[
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_VALUE])], 'type' => API_STRING_UTF8, 'in' => '', 'default' => ''],
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_LABEL])], 'type' => API_PROMETHEUS_LABEL, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_FUNCTION])], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN, ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG, ZBX_PREPROC_PROMETHEUS_COUNT])]
+ ]]
+ ]];
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_TO_JSON:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)]
+ ]];
+ break;
+
+ case ZBX_PREPROC_CSV_TO_JSON:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 1],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 1],
+ '3' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])]
+ ]];
+ break;
+
+ case ZBX_PREPROC_STR_REPLACE:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'default' => '']
+ ]];
+ break;
+ }
+
+ if (self::validate($api_input_rules, $params, $path, $error)) {
+ $data = implode("\n", $params);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePrometheusPattern(array $rule, &$data, string $path, string &$error): bool {
+ $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;
+ }
+
+ $prometheus_pattern_parser = new CPrometheusPatternParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($prometheus_pattern_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid Prometheus pattern'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePrometheusLabel(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ $prometheus_output_parser = new CPrometheusOutputParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($prometheus_output_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid Prometheus label'));
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index ead57656337..36200260754 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -1436,6 +1436,10 @@ define('API_UINTS64', 14);
define('API_CUIDS', 44);
define('API_USER_MACROS', 52);
define('API_FILTER_VALUES', 59);
+
+// any type
+define('API_ANY', 61);
+
// specific types
define('API_HG_NAME', 15);
define('API_SCRIPT_MENU_PATH', 16);
@@ -1478,6 +1482,13 @@ define('API_TIMESTAMP', 55);
define('API_TG_NAME', 56);
define('API_COLORS', 57);
define('API_FILTER', 58);
+define('API_ITEM_KEY', 62);
+define('API_ITEM_DELAY', 63);
+define('API_JSON', 64);
+define('API_XML', 65);
+define('API_PREPROC_PARAMS', 66);
+define('API_PROMETHEUS_PATTERN', 67);
+define('API_PROMETHEUS_LABEL', 68);
// flags
define('API_REQUIRED', 0x00001);
diff --git a/ui/include/items.inc.php b/ui/include/items.inc.php
index 14940303fe1..18a8dc6f86b 100644
--- a/ui/include/items.inc.php
+++ b/ui/include/items.inc.php
@@ -344,7 +344,6 @@ function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) {
*/
function interfaceType2str($type) {
$interfaceGroupLabels = [
- INTERFACE_TYPE_OPT => _('None'),
INTERFACE_TYPE_AGENT => _('Agent'),
INTERFACE_TYPE_SNMP => _('SNMP'),
INTERFACE_TYPE_JMX => _('JMX'),
@@ -397,366 +396,290 @@ function interfaceIdsByType(array $interfaces) {
}
/**
- * Copies the given items to the given hosts or templates.
+ * Create copies of items from the given sources to the given destination hosts or templates.
*
- * @param array $src_itemids Items which will be copied to $dst_hostids.
- * @param array $dst_hostids Hosts and templates to whom add items.
+ * If source type is 'templateids' or 'hostids', only non-inherited items are copied.
+ *
+ * If source type is 'itemids', all the given items are copied.
+ *
+ * @param string $src_type
+ * @param array $src_ids
+ * @param bool $dst_is_template
+ * @param array $dst_hostids
*
* @return bool
*/
-function copyItemsToHosts($src_itemids, $dst_hostids) {
- $items = API::Item()->get([
- 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
- 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint',
- 'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
- 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file',
- 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps', 'parameters'
+function copyItemsToHosts(string $src_type, array $src_ids, bool $dst_is_template, array $dst_hostids): bool {
+ $options = in_array($src_type, ['templateids', 'hostids']) ? ['inherited' => false] : [];
+
+ $src_items = API::Item()->get([
+ 'output' => ['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status',
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
],
'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
'selectTags' => ['tag', 'value'],
- 'itemids' => $src_itemids,
+ $src_type => $src_ids,
'preservekeys' => true
- ]);
-
- // Check if dependent items have master items in same selection. If not, those could be web items.
- $master_itemids = [];
+ ] + $options);
- foreach ($items as $itemid => $item) {
- if ($item['type'] == ITEM_TYPE_DEPENDENT && !array_key_exists($item['master_itemid'], $items)) {
- $master_itemids[$item['master_itemid']] = true;
- }
+ if (!$src_items) {
+ return true;
}
- // Find same master items (that includes web items) on destination host.
- $dst_master_items = [];
+ $src_itemids = array_fill_keys(array_keys($src_items), true);
+ $src_valuemapids = [];
+ $src_interfaceids = [];
+ $src_dep_items = [];
+ $dep_itemids = [];
- foreach (array_keys($master_itemids) as $master_itemid) {
- $same_master_item = get_same_item_for_host(['itemid' => $master_itemid], $dst_hostids);
-
- if ($same_master_item) {
- $dst_master_items[$master_itemid] = $same_master_item;
+ foreach ($src_items as $itemid => &$item) {
+ if ($item['valuemapid'] != 0) {
+ $src_valuemapids[$item['valuemapid']] = true;
}
- }
- $create_order = [];
- $src_itemid_to_key = [];
+ if ($item['interfaceid'] != 0) {
+ $src_interfaceids[$item['interfaceid']] = true;
+ }
- // Calculate dependency level between items so that master items are created before dependent items.
- foreach ($items as $itemid => $item) {
- $dependency_level = 0;
- $master_item = $item;
- $src_itemid_to_key[$itemid] = $item['key_'];
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if (array_key_exists($item['master_itemid'], $src_itemids)) {
+ $src_dep_items[$item['master_itemid']][] = $item;
- while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
- if (!array_key_exists($master_item['master_itemid'], $items)) {
- break;
+ unset($src_items[$itemid]);
+ }
+ else {
+ $dep_itemids[$item['master_itemid']][] = $item['itemid'];
}
-
- $master_item = $items[$master_item['master_itemid']];
- ++$dependency_level;
}
-
- $create_order[$itemid] = $dependency_level;
}
+ unset($item);
- asort($create_order);
-
- $dstHosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectInterfaces' => ['interfaceid', 'type', 'main'],
- 'hostids' => $dst_hostids,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
-
- $src_valuemapids = array_column($items, 'valuemapid', 'valuemapid');
- unset($src_valuemapids[0]);
+ $valuemap_links = [];
if ($src_valuemapids) {
- $valuemapids_map = [];
$src_valuemaps = API::ValueMap()->get([
- 'output' => ['name'],
- 'valuemapids' => $src_valuemapids,
- 'preservekeys' => true
+ 'output' => ['valuemapid', 'name'],
+ 'valuemapids' => array_keys($src_valuemapids)
]);
+
$dst_valuemaps = API::ValueMap()->get([
- 'output' => ['name', 'hostid'],
+ 'output' => ['valuemapid', 'hostid', 'name'],
'hostids' => $dst_hostids,
- 'filter' => ['name' => array_column($src_valuemaps, 'name')],
- 'preservekeys' => true
+ 'filter' => ['name' => array_unique(array_column($src_valuemaps, 'name'))]
]);
- foreach ($src_valuemaps as $src_valuemapid => $src_valuemap) {
- foreach ($dst_valuemaps as $dst_valuemapid => $dst_valuemap) {
- if ($dst_valuemap['name'] === $src_valuemap['name']) {
- $valuemapids_map[$src_valuemapid][$dst_valuemap['hostid']] = $dst_valuemapid;
- }
- }
- }
- }
+ $dst_valuemapids = [];
- foreach ($dstHosts as $dstHost) {
- $interfaceids = [];
+ foreach ($dst_valuemaps as $dst_valuemap) {
+ $dst_valuemapids[$dst_valuemap['name']][$dst_valuemap['hostid']] = $dst_valuemap['valuemapid'];
+ }
- foreach ($dstHost['interfaces'] as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interfaceids[$interface['type']] = $interface['interfaceid'];
+ foreach ($src_valuemaps as $src_valuemap) {
+ if (array_key_exists($src_valuemap['name'], $dst_valuemapids)) {
+ foreach ($dst_valuemapids[$src_valuemap['name']] as $dst_hostid => $dst_valuemapid) {
+ $valuemap_links[$src_valuemap['valuemapid']][$dst_hostid] = $dst_valuemapid;
+ }
}
}
+ }
- $itemkey_to_id = [];
- $create_items = [];
- $current_dependency = reset($create_order);
+ $interface_links = [];
+ $dst_interfaceids = [];
- foreach ($create_order as $itemid => $dependency_level) {
- if ($current_dependency != $dependency_level) {
- $current_dependency = $dependency_level;
- $created_itemids = API::Item()->create($create_items);
+ if (!$dst_is_template) {
+ $src_interfaces = [];
- if (!$created_itemids) {
- return false;
- }
- $created_itemids = $created_itemids['itemids'];
+ if ($src_interfaceids) {
+ $src_hosts = API::Host()->get([
+ 'output' => [],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ $src_type => $src_ids
+ ]);
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
+ foreach ($src_hosts as $src_host) {
+ foreach ($src_host['interfaces'] as $src_interface) {
+ if (array_key_exists($src_interface['interfaceid'], $src_interfaceids)) {
+ $src_interfaces[$src_interface['interfaceid']] =
+ array_diff_key($src_interface, array_flip(['interfaceid']));
+ }
}
-
- $create_items = [];
}
+ }
- $item = $items[$itemid];
+ $dst_hosts = API::Host()->get([
+ 'output' => ['hostid'],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ 'hostids' => $dst_hostids
+ ]);
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- $type = itemTypeInterface($item['type']);
+ foreach ($dst_hosts as $dst_host) {
+ foreach ($dst_host['interfaces'] as $dst_interface) {
+ $dst_interfaceid = $dst_interface['interfaceid'];
+ unset($dst_interface['interfaceid']);
- if ($type == INTERFACE_TYPE_ANY) {
- foreach (CItem::INTERFACE_TYPES_BY_PRIORITY as $itype) {
- if (array_key_exists($type, $interfaceids)) {
- $item['interfaceid'] = $interfaceids[$itype];
- break;
- }
+ foreach ($src_interfaces as $src_interfaceid => $src_interface) {
+ if ($src_interface == $dst_interface) {
+ $interface_links[$src_interfaceid][$dst_host['hostid']] = $dst_interfaceid;
}
}
- elseif ($type !== false && $type != INTERFACE_TYPE_OPT) {
- if (!isset($interfaceids[$type])) {
- error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'],
- $item['key_']
- ));
-
- return false;
- }
- $item['interfaceid'] = $interfaceids[$type];
+ if ($dst_interface['main'] == INTERFACE_PRIMARY) {
+ $dst_interfaceids[$dst_host['hostid']][$dst_interface['type']] = $dst_interfaceid;
}
}
+ }
+ }
- unset($item['itemid']);
-
- if ($item['valuemapid'] != 0) {
- if (array_key_exists($item['valuemapid'], $valuemapids_map)
- && array_key_exists($dstHost['hostid'], $valuemapids_map[$item['valuemapid']])) {
- $item['valuemapid'] = $valuemapids_map[$item['valuemapid']][$dstHost['hostid']];
- }
- else {
- $item['valuemapid'] = 0;
- }
- }
-
- $item['hostid'] = $dstHost['hostid'];
-
- if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- if (array_key_exists($item['master_itemid'], $items)) {
- $src_item_key = $src_itemid_to_key[$item['master_itemid']];
- $item['master_itemid'] = $itemkey_to_id[$src_item_key];
- }
- else {
- $item_found = false;
-
- if (array_key_exists($item['master_itemid'], $dst_master_items)) {
- foreach ($dst_master_items[$item['master_itemid']] as $dst_master_item) {
- if ($dst_master_item['hostid'] == $dstHost['hostid']) {
- // A matching item on destination host has been found.
+ $master_item_links = [];
- $item['master_itemid'] = $dst_master_item['itemid'];
- $item_found = true;
- }
- }
- }
+ if ($dep_itemids) {
+ $master_items = API::Item()->get([
+ 'output' => ['itemid', 'key_'],
+ 'itemids' => array_keys($dep_itemids)
+ ]);
- // Master item does not exist on destination host or has not been selected for copying.
- if (!$item_found) {
- error(_s('Item "%1$s" cannot be copied without its master item.', $item['name']));
+ $options = $dst_is_template ? ['templateids' => $dst_hostids] : ['hostids' => $dst_hostids];
- return false;
- }
- }
- }
- else {
- unset($item['master_itemid']);
- }
+ $dst_master_items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'key_'],
+ 'filter' => ['key_' => array_unique(array_column($master_items, 'key_'))]
+ ] + $options);
- $create_items[] = $item;
- }
+ $dst_master_itemids = [];
- if ($create_items && !API::Item()->create($create_items)) {
- return false;
+ foreach ($dst_master_items as $item) {
+ $dst_master_itemids[$item['hostid']][$item['key_']] = $item['itemid'];
}
- }
- return true;
-}
+ foreach ($master_items as $item) {
+ foreach ($dst_hostids as $dst_hostid) {
+ if (array_key_exists($dst_hostid, $dst_master_itemids)
+ && array_key_exists($item['key_'], $dst_master_itemids[$dst_hostid])) {
+ $master_item_links[$item['itemid']][$dst_hostid] = $dst_master_itemids[$dst_hostid][$item['key_']];
+ }
+ else {
+ $src_itemid = reset($dep_itemids[$item['itemid']]);
-function copyItems($srcHostId, $dstHostId, $assign_opt_interface = false) {
- $srcItems = API::Item()->get([
- 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
- 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'flags', 'interfaceid', 'description', 'inventory_link',
- 'jmx_endpoint', 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes',
- 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method',
- 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host',
- 'allow_traps', 'parameters'
- ],
- 'selectTags' => ['tag', 'value'],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectValueMap' => ['name'],
- 'hostids' => $srcHostId,
- 'webitems' => true,
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true
- ]);
- $dstHosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectInterfaces' => ['interfaceid', 'type', 'main'],
- 'hostids' => $dstHostId,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
- $dstHost = reset($dstHosts);
- $src_valuemap_names = [];
- $valuemap_map = [];
+ error(_s('Cannot copy item with key "%1$s" without its master item with key "%2$s".',
+ $src_items[$src_itemid]['key_'], $item['key_']
+ ));
- foreach ($srcItems as $src_item) {
- if ($src_item['valuemap'] && $src_item['templateid'] == 0) {
- $src_valuemap_names[] = $src_item['valuemap']['name'];
+ return false;
+ }
+ }
}
}
- if ($src_valuemap_names) {
- $valuemap_map = array_column(API::ValueMap()->get([
- 'output' => ['valuemapid', 'name'],
- 'hostids' => $dstHostId,
- 'filter' => ['name' => $src_valuemap_names]
- ]), 'valuemapid', 'name');
- }
-
- $create_order = [];
- $src_itemid_to_key = [];
- foreach ($srcItems as $itemid => $item) {
- $dependency_level = 0;
- $master_item = $item;
- $src_itemid_to_key[$itemid] = $item['key_'];
+ do {
+ $dst_items = [];
- while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
- $master_item = $srcItems[$master_item['master_itemid']];
- ++$dependency_level;
- }
+ foreach ($dst_hostids as $dst_hostid) {
+ foreach ($src_items as $src_item) {
+ $dst_item = array_diff_key($src_item, array_flip(['itemid']));
- $create_order[$itemid] = $dependency_level;
- }
- asort($create_order);
+ if ($src_item['valuemapid'] != 0) {
+ if (array_key_exists($src_item['valuemapid'], $valuemap_links)
+ && array_key_exists($dst_hostid, $valuemap_links[$src_item['valuemapid']])) {
+ $dst_item['valuemapid'] = $valuemap_links[$src_item['valuemapid']][$dst_hostid];
+ }
+ else {
+ $dst_item['valuemapid'] = 0;
+ }
+ }
- $itemkey_to_id = [];
- $create_items = [];
- $current_dependency = reset($create_order);
+ $dst_item['interfaceid'] = 0;
- foreach ($create_order as $itemid => $dependency_level) {
- $srcItem = $srcItems[$itemid];
+ if (!$dst_is_template) {
+ if (array_key_exists($src_item['interfaceid'], $interface_links)
+ && array_key_exists($dst_hostid, $interface_links[$src_item['interfaceid']])) {
+ $dst_item['interfaceid'] = $interface_links[$src_item['interfaceid']][$dst_hostid];
+ }
+ else {
+ $type = itemTypeInterface($src_item['type']);
+
+ if (in_array($type,
+ [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI]
+ )) {
+ if (array_key_exists($dst_hostid, $dst_interfaceids)
+ && array_key_exists($type, $dst_interfaceids[$dst_hostid])) {
+ $dst_item['interfaceid'] = $dst_interfaceids[$dst_hostid][$type];
+ }
+ else {
+ $hosts = API::Host()->get([
+ 'output' => ['host'],
+ 'hostids' => $dst_hostid
+ ]);
- // Skip creating web items. Those were created before.
- if ($srcItem['type'] == ITEM_TYPE_HTTPTEST) {
- continue;
- }
+ error(_s('Cannot find host interface on "%1$s" for item with key "%2$s".',
+ $hosts[0]['host'], $src_item['key_']
+ ));
- if ($current_dependency != $dependency_level && $create_items) {
- $current_dependency = $dependency_level;
- $created_itemids = API::Item()->create($create_items);
+ return false;
+ }
+ }
+ }
+ }
- if (!$created_itemids) {
- return false;
- }
- $created_itemids = $created_itemids['itemids'];
+ if ($src_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dst_item['master_itemid'] = $master_item_links[$src_item['master_itemid']][$dst_hostid];
+ }
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
+ $dst_items[] = ['hostid' => $dst_hostid] + $dst_item;
}
-
- $create_items = [];
}
- if ($srcItem['templateid'] != 0) {
- $srcItem = get_same_item_for_host($srcItem, $dstHost['hostid']);
+ $response = API::Item()->create($dst_items);
- if (!$srcItem) {
- return false;
- }
- $itemkey_to_id[$srcItem['key_']] = $srcItem['itemid'];
- continue;
- }
- else if ($srcItem['valuemapid'] != 0) {
- $srcItem['valuemapid'] = array_key_exists($srcItem['valuemap']['name'], $valuemap_map)
- ? $valuemap_map[$srcItem['valuemap']['name']]
- : 0;
+ if ($response === false) {
+ return false;
}
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- $interface = CItem::findInterfaceForItem($srcItem['type'], $dstHost['interfaces']);
+ $_src_items = [];
- if ($interface === false && $assign_opt_interface
- && itemTypeInterface($srcItem['type']) == INTERFACE_TYPE_OPT
- && $srcItem['interfaceid'] != 0) {
- $interface = CItem::findInterfaceByPriority($dstHost['interfaces']);
- }
+ if ($src_dep_items) {
+ foreach ($dst_hostids as $dst_hostid) {
+ foreach ($src_items as $src_item) {
+ $dst_itemid = array_shift($response['itemids']);
- if ($interface) {
- $srcItem['interfaceid'] = $interface['interfaceid'];
- }
- elseif ($interface !== false) {
- error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_']));
- }
- }
- unset($srcItem['itemid']);
- unset($srcItem['templateid']);
- $srcItem['hostid'] = $dstHostId;
-
- if (!$srcItem['preprocessing']) {
- unset($srcItem['preprocessing']);
- }
+ if (array_key_exists($src_item['itemid'], $src_dep_items)) {
+ $master_item_links[$src_item['itemid']][$dst_hostid] = $dst_itemid;
- if ($srcItem['type'] == ITEM_TYPE_DEPENDENT) {
- if ($srcItems[$srcItem['master_itemid']]['type'] == ITEM_TYPE_HTTPTEST) {
- // Web items are outside the scope and are created before regular items.
- $web_item = get_same_item_for_host($srcItems[$srcItem['master_itemid']], $dstHost['hostid']);
- $srcItem['master_itemid'] = $web_item['itemid'];
- }
- else {
- $src_item_key = $src_itemid_to_key[$srcItem['master_itemid']];
- $srcItem['master_itemid'] = $itemkey_to_id[$src_item_key];
+ $_src_items = array_merge($_src_items, $src_dep_items[$src_item['itemid']]);
+ unset($src_dep_items[$src_item['itemid']]);
+ }
+ }
}
}
- else {
- unset($srcItem['master_itemid']);
- }
-
- $create_items[] = $srcItem;
- }
- if ($create_items && !API::Item()->create($create_items)) {
- return false;
- }
+ $src_items = $_src_items;
+ } while ($src_items);
return true;
}
@@ -1292,7 +1215,7 @@ function getInterfaceSelect(array $interfaces): CSelect {
/** @var CSelectOption[] $options_by_type */
$options_by_type = [];
- $interface_select->addOption(new CSelectOption(INTERFACE_TYPE_OPT, interfaceType2str(INTERFACE_TYPE_OPT)));
+ $interface_select->addOption(new CSelectOption(0, _('None')));
foreach ($interfaces as $interface) {
$option = new CSelectOption($interface['interfaceid'], getHostInterface($interface));
@@ -2228,10 +2151,426 @@ function normalizeItemPreprocessingSteps(array $preprocessing): array {
'error_handler_params' => ''
];
- // Remove fictional field that doesn't belong in DB and API.
- unset($step['sortorder']);
+ // Remove fictional fields that don't belong to DB and API.
+ unset($step['sortorder'], $step['on_fail']);
}
unset($step);
return $preprocessing;
}
+
+/**
+ * Check that the given key is not equal to the example value presented for a specific type.
+ *
+ * @param int $type
+ * @param string $key
+ *
+ * @return bool
+ */
+function isItemExampleKey(int $type, string $key): bool {
+ if (($type == ITEM_TYPE_DB_MONITOR && $key === ZBX_DEFAULT_KEY_DB_MONITOR)
+ || ($type == ITEM_TYPE_SSH && $key === ZBX_DEFAULT_KEY_SSH)
+ || ($type == ITEM_TYPE_TELNET && $key === ZBX_DEFAULT_KEY_TELNET)) {
+ error(_('Check the key, please. Default example was passed.'));
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Check the format of the given custom intervals. Unset the custom intervals with empty values.
+ *
+ * @param array $delay_flex
+ * @param bool $lldmacros
+ */
+function isValidCustomIntervals(array &$delay_flex, bool $lldmacros = false): bool {
+ if (!$delay_flex) {
+ return true;
+ }
+
+ $simple_interval_parser = new CSimpleIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ $time_period_parser = new CTimePeriodParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ $scheduling_interval_parser = new CSchedulingIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ foreach ($delay_flex as $i => $interval) {
+ if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
+ if ($interval['delay'] === '' && $interval['period'] === '') {
+ unset($delay_flex[$i]);
+ continue;
+ }
+
+ if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['delay']));
+
+ return false;
+ }
+ elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['period']));
+
+ return false;
+ }
+ }
+ else {
+ if ($interval['schedule'] === '') {
+ unset($delay_flex[$i]);
+ continue;
+ }
+
+ if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['schedule']));
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Get all given delay intervals as string in API format.
+ *
+ * @param string $delay
+ * @param array $delay_flex
+ *
+ * @return string
+ */
+function getDelayWithCustomIntervals(string $delay, array $delay_flex): string {
+ foreach ($delay_flex as $interval) {
+ if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
+ $delay .= ';'.$interval['delay'].'/'.$interval['period'];
+ }
+ else {
+ $delay .= ';'.$interval['schedule'];
+ }
+ }
+
+ return $delay;
+}
+
+/**
+ * Format tags received via form for API input.
+ *
+ * @param array $tags Array of item tags, as received from form submit.
+ *
+ * @return array
+ */
+function prepareItemTags(array $tags): array {
+ foreach ($tags as $key => $tag) {
+ if ($tag['tag'] === '' && $tag['value'] === '') {
+ unset($tags[$key]);
+ }
+ elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
+ unset($tags[$key]);
+ }
+ else {
+ unset($tags[$key]['type']);
+ }
+ }
+
+ return $tags;
+}
+
+/**
+ * Format query fields received via form for API input.
+ *
+ * @param array $query_fields
+ *
+ * @return array
+ */
+function prepareItemQueryFields(array $query_fields): array {
+ if ($query_fields) {
+ $_query_fields = [];
+
+ foreach ($query_fields['name'] as $index => $key) {
+ $value = $query_fields['value'][$index];
+ $sortorder = $query_fields['sortorder'][$index];
+
+ if ($key !== '' || $value !== '') {
+ $_query_fields[$sortorder] = [$key => $value];
+ }
+ }
+
+ ksort($_query_fields);
+ $query_fields = array_values($_query_fields);
+ }
+
+ return $query_fields;
+}
+
+/**
+ * Format headers field received via form for API input.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+function prepareItemHeaders(array $headers): array {
+ if ($headers) {
+ $_headers = [];
+
+ foreach ($headers['name'] as $i => $name) {
+ $value = $headers['value'][$i];
+
+ if ($name === '' && $value === '') {
+ continue;
+ }
+
+ $_headers[$name] = $value;
+ }
+
+ $headers = $_headers;
+ }
+
+ return $headers;
+}
+
+/**
+ * Format parameters field received via form for API input.
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+function prepareItemParameters(array $parameters): array {
+ $_parameters = [];
+
+ if (is_array($parameters) && array_key_exists('name', $parameters)
+ && array_key_exists('value', $parameters)) {
+ foreach ($parameters['name'] as $index => $name) {
+ if (array_key_exists($index, $parameters['value'])
+ && ($name !== '' || $parameters['value'][$index] !== '')) {
+ $_parameters[] = [
+ 'name' => $name,
+ 'value' => $parameters['value'][$index]
+ ];
+ }
+ }
+ }
+
+ return $_parameters;
+}
+
+/**
+ * Get sanitized item fields of given input.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['flags']
+ * @param int $input['type']
+ * @param string $input['key_']
+ * @param int $input['value_type']
+ * @param int $input['authtype']
+ * @param int $input['allow_traps']
+ * @param int $input['hosts'][0]['status']
+ *
+ * @return array
+ */
+function getSanitizedItemFields(array $input): array {
+ $field_names = getMainItemFieldNames($input);
+
+ if ($input['flags'] != ZBX_FLAG_DISCOVERY_CREATED) {
+ $field_names = array_merge($field_names, getTypeItemFieldNames($input));
+ $field_names = getConditionalItemFieldNames($field_names, $input);
+ }
+
+ return array_intersect_key($input, array_flip($field_names));
+}
+
+/**
+ * Get main item fields of given input.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['flags']
+ *
+ * @return array
+ */
+function getMainItemFieldNames(array $input): array {
+ switch ($input['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ if ($input['templateid'] == 0) {
+ return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid',
+ 'inventory_link', 'logtimefmt', 'description', 'status', 'tags', 'preprocessing'
+ ];
+ }
+ else {
+ return ['history', 'trends', 'inventory_link', 'description', 'status', 'tags'];
+ }
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ if ($input['templateid'] == 0) {
+ return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid', 'logtimefmt',
+ 'description', 'status', 'discover', 'tags', 'preprocessing'
+ ];
+ }
+ else {
+ return ['history', 'trends', 'description', 'status', 'discover', 'tags'];
+ }
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ return ['status'];
+ }
+}
+
+/**
+ * Get item field names of the given type and template ID.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['type']
+ */
+function getTypeItemFieldNames(array $input): array {
+ switch ($input['type']) {
+ case ITEM_TYPE_ZABBIX:
+ return ['interfaceid', 'delay'];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['trapper_hosts'];
+
+ case ITEM_TYPE_SIMPLE:
+ return ['interfaceid', 'username', 'password', 'delay'];
+
+ case ITEM_TYPE_INTERNAL:
+ return ['delay'];
+
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['delay'];
+
+ case ITEM_TYPE_EXTERNAL:
+ return ['interfaceid', 'delay'];
+
+ case ITEM_TYPE_DB_MONITOR:
+ return ['username', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_IPMI:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'ipmi_sensor', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'delay'];
+ }
+
+ case ITEM_TYPE_SSH:
+ return ['interfaceid', 'authtype', 'username', 'publickey', 'privatekey', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_TELNET:
+ return ['interfaceid', 'username', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_CALCULATED:
+ return ['params', 'delay'];
+
+ case ITEM_TYPE_JMX:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'jmx_endpoint', 'username', 'password', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'username', 'password', 'delay'];
+ }
+
+ case ITEM_TYPE_SNMPTRAP:
+ return ['interfaceid'];
+
+ case ITEM_TYPE_DEPENDENT:
+ return ['master_itemid'];
+
+ case ITEM_TYPE_HTTPAGENT:
+ if ($input['templateid'] == 0) {
+ return ['url', 'query_fields', 'request_method', 'post_type', 'posts', 'headers', 'status_codes',
+ 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', 'interfaceid', 'authtype',
+ 'username', 'password', 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file',
+ 'ssl_key_password', 'timeout', 'delay', 'allow_traps', 'trapper_hosts'
+ ];
+ }
+ else {
+ return ['interfaceid', 'delay', 'allow_traps', 'trapper_hosts'];
+ }
+
+ case ITEM_TYPE_SNMP:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'snmp_oid', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'delay'];
+ }
+
+ case ITEM_TYPE_SCRIPT:
+ if ($input['templateid'] == 0) {
+ return ['parameters', 'params', 'timeout', 'delay'];
+ }
+ else {
+ return ['delay'];
+ }
+ }
+}
+
+/**
+ * Get item field names excluding those that don't match a specific conditions.
+ *
+ * @param array $field_names
+ * @param array $input
+ * @param int $input['type']
+ * @param string $input['key_']
+ * @param int $input['value_type']
+ * @param int $input['authtype']
+ * @param int $input['allow_traps']
+ * @param int $input['hosts'][0]['status']
+ *
+ * @return array
+ */
+function getConditionalItemFieldNames(array $field_names, array $input): array {
+ return array_filter($field_names, static function ($field_name) use ($input): bool {
+ switch ($field_name) {
+ case 'units':
+ case 'trends':
+ return in_array($input['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]);
+
+ case 'valuemapid':
+ return in_array($input['value_type'],
+ [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64]
+ );
+
+ case 'inventory_link':
+ return in_array($input['value_type'],
+ [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT]
+ );
+
+ case 'logtimefmt':
+ return $input['value_type'] == ITEM_VALUE_TYPE_LOG;
+
+ case 'interfaceid':
+ return in_array($input['hosts'][0]['status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+
+ case 'username':
+ case 'password':
+ return $input['type'] != ITEM_TYPE_HTTPAGENT || in_array($input['authtype'],
+ [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST]
+ );
+
+ case 'delay':
+ return $input['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($input['key_'], 'mqtt.get', 8) != 0;
+
+ case 'trapper_hosts':
+ return $input['type'] != ITEM_TYPE_HTTPAGENT || $input['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_ON;
+
+ case 'publickey':
+ case 'privatekey':
+ return $input['authtype'] == ITEM_AUTHTYPE_PUBLICKEY;
+ }
+
+ return true;
+ });
+}
diff --git a/ui/include/views/configuration.item.edit.php b/ui/include/views/configuration.item.edit.php
index 98420185b37..aefee634f76 100644
--- a/ui/include/views/configuration.item.edit.php
+++ b/ui/include/views/configuration.item.edit.php
@@ -612,7 +612,7 @@ if ($data['display_interfaces']) {
$item_tab->addItem([
(new CLabel(_('Host interface'), 'interface'))->setId('js-item-interface-label'),
(new CFormField(
- (new CTextBox('interface', interfaceType2str(INTERFACE_TYPE_OPT), true))
+ (new CTextBox('interface', _('None'), true))
->setAttribute('disabled', 'disabled')
))->setId('js-item-interface-field')
]);
diff --git a/ui/include/views/configuration.item.prototype.edit.php b/ui/include/views/configuration.item.prototype.edit.php
index 95bdb53e732..0f19cb6a33b 100644
--- a/ui/include/views/configuration.item.prototype.edit.php
+++ b/ui/include/views/configuration.item.prototype.edit.php
@@ -597,8 +597,7 @@ if ($data['display_interfaces']) {
->setValue($data['interfaceid'])
->addClass(ZBX_STYLE_ZSELECT_HOST_INTERFACE)
->setFocusableElementId('interfaceid')
- ->setAriaRequired()
- ->setReadonly(($data['type'] == ITEM_TYPE_HTTPAGENT) ? $readonly : false);
+ ->setAriaRequired();
$item_tab->addItem([
(new CLabel(_('Host interface'), $select_interface->getFocusableElementId()))
@@ -629,9 +628,12 @@ $item_tab->addItem([
$item_tab
->addItem([
- (new CLabel(_('IPMI sensor'), 'ipmi_sensor'))->setId('js-item-impi-sensor-label'),
+ (new CLabel(_('IPMI sensor'), 'ipmi_sensor'))
+ ->setAsteriskMark()
+ ->setId('js-item-impi-sensor-label'),
(new CFormField((new CTextBox('ipmi_sensor', $data['ipmi_sensor'], $readonly, 128))
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired()
))->setId('js-item-impi-sensor-field')
])
->addItem([
diff --git a/ui/items.php b/ui/items.php
index 4b495b8c466..b62fc4fcede 100644
--- a/ui/items.php
+++ b/ui/items.php
@@ -298,11 +298,6 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT && itemTypeInterface(getRequest('type')) == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
$valid_input = check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
@@ -316,28 +311,33 @@ $subfiltersList = ['subfilter_types', 'subfilter_value_types', 'subfilter_status
/*
* Permissions
*/
-$itemId = getRequest('itemid');
-if ($itemId) {
+$itemid = getRequest('itemid');
+
+if ($itemid) {
$items = API::Item()->get([
'output' => ['itemid'],
'selectHosts' => ['hostid', 'status'],
- 'itemids' => $itemId,
+ 'itemids' => $itemid,
'editable' => true
]);
+
if (!$items) {
access_deny();
}
+
$hosts = $items[0]['hosts'];
}
else {
- $hostId = getRequest('hostid');
- if ($hostId) {
+ $hostid = getRequest('hostid');
+
+ if ($hostid) {
$hosts = API::Host()->get([
'output' => ['hostid', 'status'],
- 'hostids' => $hostId,
+ 'hostids' => $hostid,
'templated_hosts' => true,
'editable' => true
]);
+
if (!$hosts) {
access_deny();
}
@@ -499,16 +499,6 @@ if (!hasRequest('form') && $filter_hostids) {
}
}
-// Convert CR+LF to LF in preprocessing script.
-if (hasRequest('preprocessing')) {
- foreach ($_REQUEST['preprocessing'] as &$step) {
- if ($step['type'] == ZBX_PREPROC_SCRIPT) {
- $step['params'][0] = CRLFtoLF($step['params'][0]);
- }
- }
- unset($step);
-}
-
// Validate backurl.
if (hasRequest('backurl') && !CHtmlUrlValidator::validateSameSite(getRequest('backurl'))) {
access_deny();
@@ -528,365 +518,142 @@ if (isset($_REQUEST['delete']) && isset($_REQUEST['itemid'])) {
show_messages($result, _('Item deleted'), _('Cannot delete item'));
}
elseif (hasRequest('add') || hasRequest('update')) {
- DBstart();
- $result = true;
-
- $delay = getRequest('delay', DB::getDefault('items', 'delay'));
- $type = getRequest('type', ITEM_TYPE_ZABBIX);
+ try {
+ $type = (int) getRequest('type', DB::getDefault('items', 'type'));
+ $key = getRequest('key', DB::getDefault('items', 'key_'));
- $tags = getRequest('tags', []);
- foreach ($tags as $key => $tag) {
- if ($tag['tag'] === '' && $tag['value'] === '') {
- unset($tags[$key]);
- }
- elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
- unset($tags[$key]);
- }
- else {
- unset($tags[$key]['type']);
+ if (isItemExampleKey($type, $key)) {
+ throw new Exception();
}
- }
- /*
- * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
- * In the end, custom intervals together with "delay" are stored in the "delay" variable.
- */
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
- && hasRequest('delay_flex')) {
- $intervals = [];
- $simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
- $time_period_parser = new CTimePeriodParser(['usermacros' => true]);
- $scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]);
-
- foreach (getRequest('delay_flex') as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
+ $delay_flex = getRequest('delay_flex', []);
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['delay']));
- break;
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['period']));
- break;
- }
-
- $intervals[] = $interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
-
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['schedule']));
- break;
- }
-
- $intervals[] = $interval['schedule'];
- }
+ if (!isValidCustomIntervals($delay_flex)) {
+ throw new Exception();
}
- if ($intervals) {
- $delay .= ';'.implode(';', $intervals);
- }
- }
+ $value_type = (int) getRequest('value_type', DB::getDefault('items', 'value_type'));
+ $trends_default = in_array($value_type, [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])
+ ? DB::getDefault('items', 'trends')
+ : 0;
+
+ $request_method = getRequest('request_method', DB::getDefault('items', 'request_method'));
+ $retrieve_mode_default = $request_method == HTTPCHECK_REQUEST_HEAD
+ ? HTTPTEST_STEP_RETRIEVE_MODE_HEADERS
+ : DB::getDefault('items', 'retrieve_mode');
+
+ $input = [
+ 'name' => getRequest('name', DB::getDefault('items', 'name')),
+ 'type' => $type,
+ 'key_' => $key,
+ 'value_type' => $value_type,
+ 'units' => getRequest('units', DB::getDefault('items', 'units')),
+ 'history' => getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('history', DB::getDefault('items', 'history')),
+ 'trends' => getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('trends', $trends_default),
+ 'valuemapid' => getRequest('valuemapid', 0),
+ 'inventory_link' => getRequest('inventory_link', DB::getDefault('items', 'inventory_link')),
+ 'logtimefmt' => getRequest('logtimefmt', DB::getDefault('items', 'logtimefmt')),
+ 'description' => getRequest('description', DB::getDefault('items', 'description')),
+ 'status' => getRequest('status', ITEM_STATUS_DISABLED),
+ 'tags' => prepareItemTags(getRequest('tags', [])),
+ 'preprocessing' => normalizeItemPreprocessingSteps(getRequest('preprocessing', [])),
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid' => getRequest('interfaceid', 0),
+ 'authtype' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_authtype', DB::getDefault('items', 'authtype'))
+ : getRequest('authtype', DB::getDefault('items', 'authtype')),
+ 'username' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_username', DB::getDefault('items', 'username'))
+ : getRequest('username', DB::getDefault('items', 'username')),
+ 'password' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_password', DB::getDefault('items', 'password'))
+ : getRequest('password', DB::getDefault('items', 'password')),
+ 'params' => getRequest('params', DB::getDefault('items', 'params')),
+ 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
+ 'delay' => getDelayWithCustomIntervals(getRequest('delay', DB::getDefault('items', 'delay')), $delay_flex),
+ 'trapper_hosts' => getRequest('trapper_hosts', DB::getDefault('items', 'trapper_hosts')),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => getRequest('master_itemid', 0),
+
+ // HTTP Agent item type specific fields.
+ 'url' => getRequest('url', DB::getDefault('items', 'url')),
+ 'query_fields' => prepareItemQueryFields(getRequest('query_fields', [])),
+ 'request_method' => $request_method,
+ 'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
+ 'posts' => getRequest('posts', DB::getDefault('items', 'posts')),
+ 'headers' => prepareItemHeaders(getRequest('headers', [])),
+ 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
+ 'follow_redirects' => getRequest('follow_redirects', DB::getDefault('items', 'follow_redirects')),
+ 'retrieve_mode' => getRequest('retrieve_mode', $retrieve_mode_default),
+ 'output_format' => getRequest('output_format', DB::getDefault('items', 'output_format')),
+ 'http_proxy' => getRequest('http_proxy', DB::getDefault('items', 'http_proxy')),
+ 'verify_peer' => getRequest('verify_peer', DB::getDefault('items', 'verify_peer')),
+ 'verify_host' => getRequest('verify_host', DB::getDefault('items', 'verify_host')),
+ 'ssl_cert_file' => getRequest('ssl_cert_file', DB::getDefault('items', 'ssl_cert_file')),
+ 'ssl_key_file' => getRequest('ssl_key_file', DB::getDefault('items', 'ssl_key_file')),
+ 'ssl_key_password' => getRequest('ssl_key_password', DB::getDefault('items', 'ssl_key_password')),
+ 'allow_traps' => getRequest('allow_traps', DB::getDefault('items', 'allow_traps')),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => getRequest('ipmi_sensor', DB::getDefault('items', 'ipmi_sensor')),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => getRequest('jmx_endpoint', DB::getDefault('items', 'jmx_endpoint')),
+
+ // Script item type specific fields.
+ 'parameters' => prepareItemParameters(getRequest('parameters', [])),
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => getRequest('snmp_oid', DB::getDefault('items', 'snmp_oid')),
+
+ // SSH item type specific fields.
+ 'publickey' => getRequest('publickey', DB::getDefault('items', 'publickey')),
+ 'privatekey' => getRequest('privatekey', DB::getDefault('items', 'privatekey'))
+ ];
- if ($result) {
- $preprocessing = getRequest('preprocessing', []);
- $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- $http_item = [
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
- 'url' => getRequest('url'),
- 'query_fields' => getRequest('query_fields', []),
- 'posts' => getRequest('posts'),
- 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
- 'follow_redirects' => (int) getRequest('follow_redirects'),
- 'post_type' => (int) getRequest('post_type'),
- 'http_proxy' => getRequest('http_proxy'),
- 'headers' => getRequest('headers', []),
- 'retrieve_mode' => (int) getRequest('retrieve_mode'),
- 'request_method' => (int) getRequest('request_method'),
- 'output_format' => (int) getRequest('output_format'),
- 'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
- 'ssl_cert_file' => getRequest('ssl_cert_file'),
- 'ssl_key_file' => getRequest('ssl_key_file'),
- 'ssl_key_password' => getRequest('ssl_key_password'),
- 'verify_peer' => (int) getRequest('verify_peer'),
- 'verify_host' => (int) getRequest('verify_host'),
- 'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
- 'username' => getRequest('http_username', ''),
- 'password' => getRequest('http_password', '')
- ];
- }
+ $result = true;
if (hasRequest('add')) {
- $item = [
- 'hostid' => getRequest('hostid'),
- 'name' => getRequest('name', ''),
- 'type' => getRequest('type', ITEM_TYPE_ZABBIX),
- 'key_' => getRequest('key', ''),
- 'interfaceid' => getRequest('interfaceid', 0),
- 'snmp_oid' => getRequest('snmp_oid', ''),
- 'authtype' => getRequest('authtype', ITEM_AUTHTYPE_PASSWORD),
- 'username' => getRequest('username', ''),
- 'password' => getRequest('password', ''),
- 'publickey' => getRequest('publickey', ''),
- 'privatekey' => getRequest('privatekey', ''),
- 'params' => getRequest('params', ''),
- 'ipmi_sensor' => getRequest('ipmi_sensor', ''),
- 'value_type' => getRequest('value_type', ITEM_VALUE_TYPE_FLOAT),
- 'units' => getRequest('units', ''),
- 'delay' => $delay,
- 'history' => (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('history', DB::getDefault('items', 'history')),
- 'trends' => (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('trends', DB::getDefault('items', 'trends')),
- 'valuemapid' => getRequest('valuemapid', 0),
- 'logtimefmt' => getRequest('logtimefmt', ''),
- 'trapper_hosts' => getRequest('trapper_hosts', ''),
- 'inventory_link' => getRequest('inventory_link', 0),
- 'description' => getRequest('description', ''),
- 'status' => getRequest('status', ITEM_STATUS_DISABLED),
- 'tags' => $tags
- ];
-
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
+ $item = ['hostid' => $hostid];
- if ($item['type'] == ITEM_TYPE_JMX) {
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- }
-
- if ($preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
-
- if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = getRequest('master_itemid');
- }
-
- if ($item['type'] == ITEM_TYPE_SCRIPT) {
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
+ $item += getSanitizedItemFields($input + [
+ 'templateid' => '0',
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hosts' => $hosts
+ ]);
- $item = prepareScriptItemFormData($script_item) + $item;
- }
+ $response = API::Item()->create($item);
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- unset($item['valuemapid']);
+ if ($response === false) {
+ throw new Exception();
}
-
- $result = (bool) API::Item()->create($item);
}
- // Update
- else {
+
+ if (hasRequest('update')) {
$db_items = API::Item()->get([
- 'output' => ['name', 'type', 'key_', 'interfaceid', 'snmp_oid', 'authtype', 'username', 'password',
- 'publickey', 'privatekey', 'params', 'ipmi_sensor', 'value_type', 'units', 'delay', 'history',
- 'trends', 'valuemapid', 'logtimefmt', 'trapper_hosts', 'inventory_link', 'description', 'status',
- 'templateid', 'flags', 'jmx_endpoint', 'master_itemid', 'timeout', 'url', 'query_fields', 'posts',
- 'status_codes', 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode',
- 'request_method', 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password',
- 'verify_peer', 'verify_host', 'allow_traps', 'parameters'
- ],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => getRequest('itemid')
+ 'output' => ['templateid', 'flags', 'type', 'key_', 'value_type', 'authtype', 'allow_traps'],
+ 'itemids' => $itemid
]);
- $db_item = reset($db_items);
-
- $item = [];
-
- if ($db_item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- if ($db_item['templateid'] == 0) {
- $value_type = getRequest('value_type', ITEM_VALUE_TYPE_FLOAT);
-
- if ($db_item['name'] !== getRequest('name', '')) {
- $item['name'] = getRequest('name', '');
- }
- if ($db_item['type'] != getRequest('type', ITEM_TYPE_ZABBIX)) {
- $item['type'] = getRequest('type', ITEM_TYPE_ZABBIX);
- }
- if ($db_item['key_'] !== getRequest('key', '')) {
- $item['key_'] = getRequest('key', '');
- }
- if ($db_item['snmp_oid'] !== getRequest('snmp_oid', '')) {
- $item['snmp_oid'] = getRequest('snmp_oid', '');
- }
- if ($db_item['ipmi_sensor'] !== getRequest('ipmi_sensor', '')) {
- $item['ipmi_sensor'] = getRequest('ipmi_sensor', '');
- }
- if ($db_item['value_type'] != $value_type) {
- $item['value_type'] = $value_type;
- }
- if ($db_item['units'] !== getRequest('units', '')) {
- $item['units'] = getRequest('units', '');
- }
- if ($value_type != ITEM_VALUE_TYPE_LOG && $value_type != ITEM_VALUE_TYPE_TEXT
- && bccomp($db_item['valuemapid'], getRequest('valuemapid', 0)) != 0) {
- $item['valuemapid'] = getRequest('valuemapid', 0);
- }
- if ($db_item['logtimefmt'] !== getRequest('logtimefmt', '')) {
- $item['logtimefmt'] = getRequest('logtimefmt', '');
- }
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- if ($db_item['preprocessing'] !== $preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
- }
-
- if (bccomp($db_item['interfaceid'], getRequest('interfaceid', 0)) != 0) {
- $item['interfaceid'] = getRequest('interfaceid', 0);
- }
-
- if ($db_item['delay'] != $delay) {
- $item['delay'] = $delay;
- }
- $def_item_history = (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : DB::getDefault('items', 'history');
- if ((string) $db_item['history'] !== (string) getRequest('history', $def_item_history)) {
- $item['history'] = getRequest('history', $def_item_history);
- }
- $def_item_trends = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : DB::getDefault('items', 'trends');
- if ((string) $db_item['trends'] !== (string) getRequest('trends', $def_item_trends)) {
- $item['trends'] = getRequest('trends', $def_item_trends);
- }
- if ($db_item['trapper_hosts'] !== getRequest('trapper_hosts', '')) {
- $item['trapper_hosts'] = getRequest('trapper_hosts', '');
- }
- if ($db_item['jmx_endpoint'] !== getRequest('jmx_endpoint', '')) {
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- }
- if ($db_item['inventory_link'] != getRequest('inventory_link', 0)) {
- $item['inventory_link'] = getRequest('inventory_link', 0);
- }
- if ($db_item['description'] !== getRequest('description', '')) {
- $item['description'] = getRequest('description', '');
- }
-
- if ($db_item['templateid'] == 0 && $type == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
- }
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- if ($db_item['authtype'] != getRequest('http_authtype', ITEM_AUTHTYPE_PASSWORD)) {
- $item['authtype'] = getRequest('http_authtype', ITEM_AUTHTYPE_PASSWORD);
- }
-
- if ($db_item['username'] !== getRequest('http_username', '')) {
- $item['username'] = getRequest('http_username', '');
- }
-
- if ($db_item['password'] !== getRequest('http_password', '')) {
- $item['password'] = getRequest('http_password', '');
- }
- }
- else {
- if ($db_item['authtype'] != getRequest('authtype', ITEM_AUTHTYPE_PASSWORD)) {
- $item['authtype'] = getRequest('authtype', ITEM_AUTHTYPE_PASSWORD);
- }
-
- if ($db_item['username'] !== getRequest('username', '')) {
- $item['username'] = getRequest('username', '');
- }
- if ($db_item['password'] !== getRequest('password', '')) {
- $item['password'] = getRequest('password', '');
- }
- }
+ $item = getSanitizedItemFields($input + $db_items[0] + ['hosts' => $hosts]);
- if ($db_item['publickey'] !== getRequest('publickey', '')) {
- $item['publickey'] = getRequest('publickey', '');
- }
+ $response = API::Item()->update(['itemid' => $itemid] + $item);
- if ($db_item['privatekey'] !== getRequest('privatekey', '')) {
- $item['privatekey'] = getRequest('privatekey', '');
- }
-
- if ($db_item['status'] != getRequest('status', ITEM_STATUS_DISABLED)) {
- $item['status'] = getRequest('status', ITEM_STATUS_DISABLED);
- }
-
- if (getRequest('type') == ITEM_TYPE_DEPENDENT && hasRequest('master_itemid')
- && bccomp($db_item['master_itemid'], getRequest('master_itemid')) != 0) {
- $item['master_itemid'] = getRequest('master_itemid');
- }
-
- if (getRequest('type') == ITEM_TYPE_SCRIPT) {
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
-
- $item = prepareScriptItemFormData($script_item) + $item;
- if ($db_item['type'] == getRequest('type')) {
- $compare = function($arr, $arr2) {
- return (array_combine(array_column($arr, 'name'), array_column($arr, 'value')) ==
- array_combine(array_column($arr2, 'name'), array_column($arr2, 'value'))
- );
- };
-
- if ($compare($db_item['parameters'], $item['parameters'])) {
- unset($item['parameters']);
- }
- if ($db_item['timeout'] === $item['timeout']) {
- unset($item['timeout']);
- }
-
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- }
- else {
- // If type is changed, even if value stays the same, it must be set. It is required by API.
- $item['params'] = getRequest('params', '');
- }
- }
- else {
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- }
-
- CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
- CArrayHelper::sort($tags, ['tag', 'value']);
-
- if (array_values($db_item['tags']) !== array_values($tags)) {
- $item['tags'] = $tags;
- }
-
- if ($item) {
- $item['itemid'] = getRequest('itemid');
-
- $result = (bool) API::Item()->update($item);
- }
- else {
- $result = true;
+ if ($response === false) {
+ throw new Exception();
}
}
}
-
- $result = DBend($result);
+ catch (Exception $e) {
+ $result = false;
+ }
if (hasRequest('add')) {
show_messages($result, _('Item added'), _('Cannot add item'));
@@ -940,41 +707,52 @@ elseif (hasRequest('action') && str_in_array(getRequest('action'), ['item.massen
elseif (hasRequest('action') && getRequest('action') === 'item.masscopyto' && hasRequest('copy')
&& hasRequest('group_itemid')) {
if (getRequest('copy_targetids', []) && hasRequest('copy_type')) {
- if (getRequest('copy_type') == COPY_TYPE_TO_HOST || getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE) {
- $hostids = getRequest('copy_targetids');
- }
- elseif (getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE_GROUP) {
+ if (in_array(getRequest('copy_type'), [COPY_TYPE_TO_TEMPLATE, COPY_TYPE_TO_TEMPLATE_GROUP])) {
+ $options = getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE
+ ? ['templateids' => getRequest('copy_targetids')]
+ : ['groupids' => getRequest('copy_targetids')];
+
$hostids = array_keys(API::Template()->get([
'output' => [],
- 'groupids' => getRequest('copy_targetids'),
'editable' => true,
'preservekeys' => true
- ]));
+ ] + $options));
+
+ $dst_is_template = true;
}
- else {
+
+ if (in_array(getRequest('copy_type'), [COPY_TYPE_TO_HOST, COPY_TYPE_TO_HOST_GROUP])) {
+ $options = getRequest('copy_type') == COPY_TYPE_TO_HOST
+ ? ['hostids' => getRequest('copy_targetids')]
+ : ['groupids' => getRequest('copy_targetids')];
+
$hostids = array_keys(API::Host()->get([
'output' => [],
- 'groupids' => getRequest('copy_targetids'),
'editable' => true,
'preservekeys' => true
- ]));
+ ] + $options));
+
+ $dst_is_template = false;
}
- DBstart();
+ $result = true;
- $result = copyItemsToHosts(getRequest('group_itemid'), $hostids);
- $result = DBend($result);
+ if ($hostids) {
+ DBstart();
+ $result = copyItemsToHosts('itemids', getRequest('group_itemid'), $dst_is_template, $hostids);
+ DBend($result);
+ }
$items_count = count(getRequest('group_itemid'));
+ show_messages($result, _n('Item copied', 'Items copied', $items_count),
+ _n('Cannot copy item', 'Cannot copy items', $items_count)
+ );
+
if ($result) {
uncheckTableRows(getRequest('checkbox_hash'));
unset($_REQUEST['group_itemid']);
}
- show_messages($result,
- _n('Item copied', 'Items copied', $items_count),
- _n('Cannot copy item', 'Cannot copy items', $items_count)
- );
}
else {
show_error_message(_('No target selected.'));
diff --git a/ui/js/pages/items.js b/ui/js/pages/items.js
index 4614da27cb7..75d997a0ede 100644
--- a/ui/js/pages/items.js
+++ b/ui/js/pages/items.js
@@ -97,8 +97,8 @@ function organizeInterfaces(interface_ids_by_types, item_interface_types, item_t
const allowed_opt_interface = (interface_type == INTERFACE_TYPE_OPT);
- $(interface_select_node.getOptionByValue(INTERFACE_TYPE_OPT)).attr('disabled', !allowed_opt_interface);
- $interface_select.find('li[value="'+INTERFACE_TYPE_OPT+'"]')
+ $(interface_select_node.getOptionByValue(0)).attr('disabled', !allowed_opt_interface);
+ $interface_select.find('li[value="0"]')
.toggle(allowed_opt_interface)
.parents('li[optgroup]:first')
.toggle(allowed_opt_interface);
diff --git a/ui/templates.php b/ui/templates.php
index 7b9038f30bb..604a122d48e 100644
--- a/ui/templates.php
+++ b/ui/templates.php
@@ -333,7 +333,7 @@ elseif (hasRequest('add') || hasRequest('update')) {
throw new Exception();
}
- if (!copyItems($cloneTemplateId, $input_templateid, true)) {
+ if (!copyItemsToHosts('templateids', [$cloneTemplateId], true, [$input_templateid])) {
throw new Exception();
}
diff --git a/ui/tests/api_json/ApiJsonTests.php b/ui/tests/api_json/ApiJsonTests.php
index 34a47cba88e..b58891a4837 100644
--- a/ui/tests/api_json/ApiJsonTests.php
+++ b/ui/tests/api_json/ApiJsonTests.php
@@ -55,7 +55,7 @@ require_once dirname(__FILE__).'/testValuemap.php';
require_once dirname(__FILE__).'/testWebScenario.php';
require_once dirname(__FILE__).'/testMap.php';
require_once dirname(__FILE__).'/testDiscoveryRule.php';
-require_once dirname(__FILE__).'/testDependentItems.php';
+// require_once dirname(__FILE__).'/testDependentItems.php';
require_once dirname(__FILE__).'/testAuthentication.php';
require_once dirname(__FILE__).'/testAuditlogAction.php';
require_once dirname(__FILE__).'/testAuditlogAutoregistration.php';
@@ -81,7 +81,7 @@ class ApiJsonTests {
$suite->addTestSuite('testAPIInfo');
$suite->addTestSuite('testAction');
$suite->addTestSuite('testConfiguration');
-// $suite->addTestSuite('testDependentItems'); TODO: To be fixed later
+ // $suite->addTestSuite('testDependentItems');
$suite->addTestSuite('testCorrelation');
$suite->addTestSuite('testDRule');
$suite->addTestSuite('testGraphPrototype');
diff --git a/ui/tests/api_json/data/data_test.sql b/ui/tests/api_json/data/data_test.sql
index 6018f00553e..63d1b9602bc 100644
--- a/ui/tests/api_json/data/data_test.sql
+++ b/ui/tests/api_json/data/data_test.sql
@@ -212,15 +212,15 @@ INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,his
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150148, 50009, 1, 9, 0,'Download speed for scenario "Api web scenario for delete1".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150149, 50009, 1, 9, 0,'Download speed for scenario "Api step for delete0".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150150, 50009, 1, 9, 0,'Download speed for scenario "Api web scenario for delete0".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150151, 50009, 1, 9, 0,'Download speed for scenario "$1".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150152, 50009, 1, 9, 3,'Failed step of scenario "$1".','web.test.fail[Webtest key_name]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150153, 50009, 1, 9, 1,'Last error message of scenario "$1".','web.test.error[Webtest key_name]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150154, 50009, 1, 9, 0,'Download speed for step "$2" of scenario "$1".','web.test.in[Webtest key_name,Webstep name 1,bps]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150155, 50009, 1, 9, 0,'Response time for step "$2" of scenario "$1".','web.test.time[Webtest key_name,Webstep name 1,resp]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150156, 50009, 1, 9, 3,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Webtest key_name,Webstep name 1]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150157, 50009, 1, 9, 0,'Download speed for step "$2" of scenario "$1".','web.test.in[Webtest key_name,Webstep name 2,bps]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150158, 50009, 1, 9, 0,'Response time for step "$2" of scenario "$1".','web.test.time[Webtest key_name,Webstep name 2,resp]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150159, 50009, 1, 9, 3,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Webtest key_name,Webstep name 2]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150151, 50009, 1, 9, 0,'Download speed for scenario "Webtest key_name".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150152, 50009, 1, 9, 3,'Failed step of scenario "Webtest key_name".','web.test.fail[Webtest key_name]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150153, 50009, 1, 9, 1,'Last error message of scenario "Webtest key_name".','web.test.error[Webtest key_name]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150154, 50009, 1, 9, 0,'Download speed for step "Webstep name 1" of scenario "Webtest key_name".','web.test.in[Webtest key_name,Webstep name 1,bps]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150155, 50009, 1, 9, 0,'Response time for step "Webstep name 1" of scenario "Webtest key_name".','web.test.time[Webtest key_name,Webstep name 1,resp]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150156, 50009, 1, 9, 3,'Response code for step "Webstep name 1" of scenario "Webtest key_name".','web.test.rspcode[Webtest key_name,Webstep name 1]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150157, 50009, 1, 9, 0,'Download speed for step "Webstep name 2" of scenario "Webtest key_name".','web.test.in[Webtest key_name,Webstep name 2,bps]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150158, 50009, 1, 9, 0,'Response time for step "Webstep name 2" of scenario "Webtest key_name".','web.test.time[Webtest key_name,Webstep name 2,resp]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150159, 50009, 1, 9, 3,'Response code for step "Webstep name 2" of scenario "Webtest key_name".','web.test.rspcode[Webtest key_name,Webstep name 2]','1m','30d',0,'','',0,'','');
INSERT INTO httpstepitem (httpstepitemid, httpstepid, itemid, type) VALUES (150143, 15010, 150143, 2);
INSERT INTO httptestitem (httptestitemid, httptestid, itemid, type) VALUES (150144, 15010, 150144, 4);
INSERT INTO httpstepitem (httpstepitemid, httpstepid, itemid, type) VALUES (150145, 15005, 150145, 2);
diff --git a/ui/tests/api_json/testAuditlogScheduledReport.php b/ui/tests/api_json/testAuditlogScheduledReport.php
index 4365e1111f6..4dd96f8861a 100755
--- a/ui/tests/api_json/testAuditlogScheduledReport.php
+++ b/ui/tests/api_json/testAuditlogScheduledReport.php
@@ -101,7 +101,6 @@ class testAuditlogScheduledReport extends testAuditlogCommon {
'report.users['.self::$before_user['reportuserid'].'].reportuserid' => ['add', self::$before_user['reportuserid']],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].']' => ['add'],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].usrgrpid' => ['add', '7'],
- 'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].access_userid' => ['add', '0'],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].reportusrgrpid'
=> ['add', self::$before_usrgrp['reportusrgrpid']],
'report.reportid' => ['add', self::$resourceid]
@@ -167,7 +166,6 @@ class testAuditlogScheduledReport extends testAuditlogCommon {
'report.status' => ['update', '0', '1'],
'report.description' => ['update', 'Updated description', 'Report description'],
'report.users['.$user['reportuserid'].'].userid' => ['add', '2'],
- 'report.users['.$user['reportuserid'].'].access_userid' => ['add', '0'],
'report.users['.$user['reportuserid'].'].exclude' => ['add', '1'],
'report.users['.$user['reportuserid'].'].reportuserid' => ['add', $user['reportuserid']],
'report.user_groups['.$usrgrp['reportusrgrpid'].'].usrgrpid' => ['add', '8'],
diff --git a/ui/tests/api_json/testDependentItems.php b/ui/tests/api_json/testDependentItems.php
index 878f03c631d..56d510d1af3 100644
--- a/ui/tests/api_json/testDependentItems.php
+++ b/ui/tests/api_json/testDependentItems.php
@@ -33,9 +33,9 @@ class testDependentItems extends CAPITest {
$items[] = [
'hostid' => $hostid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -51,9 +51,9 @@ class testDependentItems extends CAPITest {
'hostid' => $hostid,
'ruleid' => $ruleid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
- 'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'type' => ITEM_TYPE_DEPENDENT,
+ 'key_' => $prefix.'.'.$i.'[{#LLD}]',
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -68,9 +68,9 @@ class testDependentItems extends CAPITest {
$items[] = [
'hostid' => $hostid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -79,252 +79,224 @@ class testDependentItems extends CAPITest {
}
public static function getTestCases() {
+ $dep_count_overflow = ZBX_DEPENDENT_ITEM_MAX_COUNT + 1;
+
return [
- // Simple update master item.
- [
+ 'Simple update master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Simple update master item prototype.
- [
+ 'Simple update master item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1018 // dependent.items.template.1:master.item.proto.1
]
],
- // Simple update discovered master item.
- [
+ 'Simple update discovered master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 2304 // dependent.items.host.7:net.if[eth0]
]
],
- // Simple update dependent item.
- [
+ 'Simple update dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1015 // dependent.items.template.1:dependent.item.1.2.2.2
]
],
- // Simple update dependent item prototype.
- [
+ 'Simple update dependent item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1032 // dependent.items.template.1:dependent.item.proto.1.2.2.2
]
],
- // Simple update discovered dependent item.
- [
+ 'Simple update discovered dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 2305 // dependent.items.host.7:net.if.in[eth0]
]
],
- // Simple update dependent discovery rule.
- [
+ 'Simple update dependent discovery rule.' => [
'error' => null,
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1034 // dependent.items.template.1:dependent.discovery.rule.1.1
]
],
- // Set incorrect master_itemid for item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.create',
// 1015: dependent.items.host.8
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getItems(1015, 2499, 'dependent.item.1', 2, 2)
],
- // Set incorrect master_itemid for item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.update',
'request_data' => [
'itemid' => 2402, // dependent.items.host.8:dependent.item.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set incorrect master_itemid for item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getItemPrototypes(1015, 2403, 2499, 'dependent.item.proto.1', 2, 2)
],
- // Set incorrect master_itemid for item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set incorrect master_itemid for discovery rule (create).
- [
+ 'Set incorrect master_itemid for discovery rule (create).' => [
'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
'method' => 'discoveryrule.create',
// 1015: dependent.items.host.8
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getDiscoveryRule(1015, 2499, 'dependent.discovery.rule.1', 2, 2)
],
- // Set incorrect master_itemid for discovery rule (update).
- [
+ 'Set incorrect master_itemid for discovery rule (update).' => [
'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 2409, // dependent.items.host.8:dependent.discovery.rule.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set master_itemid from other host for item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item ID from another host or template.',
'method' => 'item.create',
// 1015: dependent.items.host.8
// 2501: dependent.items.host.9:master.item.1
'request_data' => self::getItems(1015, 2501, 'dependent.item.1', 2, 2)
],
- // Set master_itemid from other host for item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item ID from another host or template.',
'method' => 'item.update',
'request_data' => [
'itemid' => 2402, // dependent.items.host.8:dependent.item.1.1
'master_itemid' => 2501 // dependent.items.host.9:master.item.1
]
],
- // Set master_itemid from other host for item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item/item prototype ID from another host or template.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
// 2504: dependent.items.host.9:master.item.proto.1
'request_data' => self::getItemPrototypes(1015, 2403, 2504, 'dependent.item.proto.1', 2, 2)
],
- // Set master_itemid from other host for item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item/item prototype ID from another host or template.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
'master_itemid' => 2504 // dependent.items.host.9:master.item.proto.1
]
],
- // Set master_itemid from other discovery rule (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": ruleid of dependent item and master item should match.',
+ 'Set master_itemid from other discovery rule (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item prototype ID from another LLD rule.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
// 2407: dependent.items.host.8:master.item.proto.2
'request_data' => self::getItemPrototypes(1015, 2403, 2407, 'dependent.item.proto.1', 2, 2)
],
- // Set master_itemid from other discovery rule (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": ruleid of dependent item and master item should match.',
+ 'Set master_itemid from other discovery rule (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item prototype ID from another LLD rule.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
'master_itemid' => 2407 // dependent.items.host.8:master.item.proto.2
]
],
- // Set master_itemid from other host for discovery rule (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for discovery rule (create).' => [
+ 'error' => 'Incorrect value for field "master_itemid": "hostid" of dependent item and master item should match.',
'method' => 'discoveryrule.create',
// 1015: dependent.items.host.8
// 2501: dependent.items.host.9:master.item.1
'request_data' => self::getDiscoveryRule(1015, 2501, 'dependent.discovery.rule.1', 2, 2)
],
- // Set master_itemid from other host for discovery rule (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for discovery rule (update).' => [
+ 'error' => 'Incorrect value for field "master_itemid": "hostid" of dependent item and master item should match.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 2409, // dependent.items.host.8:dependent.discovery.rule.1.1
'master_itemid' => 2501 // dependent.items.host.9:master.item.1
]
],
- // Create dependent item, which depends on discovered item.
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
+ 'Create dependent item, which depends on discovered item.' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.create',
// 1014: dependent.items.host.7
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getItems(1014, 2304, 'item', 1, 1)
],
- // Create dependent item prototype, which depends on discovered item.
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
+ 'Create dependent item prototype, which depends on discovered item.' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.create',
// 1014: dependent.items.host.7
// 2301: dependent.items.host.7:net.if.discovery
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getItemPrototypes(1014, 2301, 2304, 'item.proto', 1, 1)
],
- // Create dependent discovery rule, which depends on discovered item.
- [
+ 'Create dependent discovery rule, which depends on discovered item.' => [
'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
'method' => 'discoveryrule.create',
// 1014: dependent.items.host.7
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getDiscoveryRule(1014, 2304, 'discovery.rule', 1, 1)
],
- // Simple update templated master item.
- [
+ 'Simple update templated master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1301 // dependent.items.host.1:master.item.1
]
],
- // Simple update templated master item prototype.
- [
+ 'Simple update templated master item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1318 // dependent.items.host.1:master.item.proto.1
]
],
- // Simple update templated dependent item.
- [
+ 'Simple update templated dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1315 // dependent.items.host.1:dependent.item.1.2.2.2
]
],
- // Simple update templated dependent item prototype.
- [
+ 'Simple update templated dependent item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1332 // dependent.items.host.1:dependent.item.proto.1.2.2.2
]
],
- // Simple update templated dependent discovery rule.
- [
+ 'Simple update templated dependent discovery rule.' => [
'error' => null,
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1334 // dependent.items.host.1:dependent.discovery.rule.1.1
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
'method' => 'item.update',
'request_data' => [
@@ -332,8 +304,7 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1015
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
'method' => 'itemprototype.update',
'request_data' => [
@@ -341,8 +312,7 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1032
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": Item "1034" does not exist or you have no access to this item.',
'method' => 'discoveryrule.update',
'request_data' => [
@@ -350,53 +320,48 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1034
]
],
- // Circular dependency to between several items (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
+ 'Circular dependency to between several items (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": circular item dependency is not allowed.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1003, // dependent.items.template.1:dependent.item.1.2
'master_itemid' => 1015
]
],
- // Circular dependency to between several item prototypes (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
+ 'Circular dependency to between several item prototypes (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": circular item dependency is not allowed.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1020, // dependent.items.template.1:dependent.item.proto.1.2
'master_itemid' => 1032
]
],
- // Set "master_itemid" for not-dependent item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'item.create',
'request_data' => [
'hostid' => 1001, // dependent.items.template.1
'name' => 'trap.2',
- 'type' => 2, // ITEM_TYPE_TRAPPER
+ 'type' => ITEM_TYPE_TRAPPER,
'key_' => 'trap.2',
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'itemprototype.create',
'request_data' => [
'hostid' => 1001, // dependent.items.template.1
'ruleid' => 1017, // dependent.items.template.1:discovery.rule.1
'name' => 'item.proto.2',
- 'type' => 2, // ITEM_TYPE_TRAPPER
- 'key_' => 'item.proto.2',
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'key_' => 'item.proto.2[{#LLD}]',
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent discovery rule (create).
- [
+ 'Set "master_itemid" for not-dependent discovery rule (create).' => [
'error' => 'Incorrect value for field "master_itemid": should be empty.',
'method' => 'discoveryrule.create',
'request_data' => [
@@ -407,26 +372,23 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1016, // dependent.items.template.1:trap.1
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1033, // dependent.items.template.1:item.proto.1
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent discovery rule (update).
- [
+ 'Set "master_itemid" for not-dependent discovery rule (update).' => [
'error' => 'Incorrect value for field "master_itemid": should be empty.',
'method' => 'discoveryrule.update',
'request_data' => [
@@ -434,290 +396,295 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Check for maximum depth for the items tree (create). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth for the items tree (create). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1.1.1.1" on the master item with key "dependent.item.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1008: dependent.items.template.1:dependent.item.1.1.1.1
'request_data' => self::getItems(1001, 1008, 'dependent.item.1.1.1.1', 1, 1)
],
- // Check for maximum depth for the item prototypes tree (create). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth for the item prototypes tree (create). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.1.1.1.1.1[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1025: dependent.items.template.1:dependent.item.proto.1.1.1.1
'request_data' => self::getItemPrototypes(1001, 1017, 1025, 'dependent.item.1.1.1.1', 1, 1)
],
- // Check for maximum depth of the discovery rule tree (create). Add 4th level.
- [
+ 'Check for maximum depth of the discovery rule tree (create). Add 4th level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1008: dependent.items.template.1:dependent.item.1.1.1.1
'request_data' => self::getDiscoveryRule(1001, 1008, 'dependent.discovery.rule.1.1.1.1', 1, 1)
],
- // Check for maximum depth of the items tree (update). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the items tree (update). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item with key "trap.1" on the master item with key "dependent.item.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1016, // dependent.items.template.1:trap.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1008 // dependent.items.template.1:dependent.item.1.1.1.1
]
],
- // Check for maximum depth of the item prototypes tree (update). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the item prototypes tree (update). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "item.proto.1" on the master item prototype with key "dependent.item.proto.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1033, // dependent.items.template.1:item.proto.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1025 // dependent.items.template.1:dependent.item.proto.1.1.1.1
]
],
- // Check for maximum depth of the discovery rule tree (update). Add 4th level.
- [
+ 'Check for maximum depth of the discovery rule tree (update). Add 4th level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1017, // dependent.items.template.1:discovery.rule.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1008 // dependent.items.template.1:dependent.item.1.1.1.1
]
],
- // Check for maximum depth of the items tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the items tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item with key "item.2" on the master item with key "item.1" on the host "dependent.items.host.4": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1702, // dependent.items.template.4:item.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1701 // dependent.items.template.4:item.1
]
],
- // Check for maximum depth of the mixed tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the mixed tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item with key "item.2" on the master item with key "item.1" on the host "dependent.items.host.5": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1902, // dependent.items.template.5:item.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1901 // dependent.items.template.5:item.1
]
],
- // Check for maximum depth of the item prototypes tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the item prototypes tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "item.proto.2" on the master item prototype with key "item.proto.1" on the host "dependent.items.host.6": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2103, // dependent.items.template.6:item.proto.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 2102 // dependent.items.template.6:item.proto.1
]
],
- // Check for maximum depth of the items tree (link a template).
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
- 'method' => 'template.update',
+ 'Check for maximum depth of the items tree (link a template).' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1" on the master item with key "master.item.1" on the host "dependent.items.host.2": allowed count of dependency levels would be exceeded.',
+ 'method' => 'host.update',
'request_data' => [
- 'templateid' => 1005, // dependent.items.template.2
- 'hosts' => [
- ['hostid' => 1006] // dependent.items.host.2
+ 'hostid' => 1006, // dependent.items.host.2
+ 'templates' => [
+ 'templateid' => 1005 // dependent.items.template.2
]
]
],
- // Check for maximum depth of the mixed tree (link a template).
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
- 'method' => 'template.update',
+ 'Check for maximum depth of the mixed tree (link a template).' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1" on the master item with key "master.item.1" on the host "dependent.items.host.3": allowed count of dependency levels would be exceeded.',
+ 'method' => 'host.update',
'request_data' => [
- 'templateid' => 1005, // dependent.items.template.2
- 'hosts' => [
- ['hostid' => 1007] // dependent.items.host.3
+ 'hostid' => 1007, // dependent.items.host.3
+ 'templates' => [
+ 'templateid' => 1005 // dependent.items.template.2
]
]
],
- // Check for maximum count of items in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.3" on the master item with key "master.item.1" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getItems(1001, 1001, 'dependent.item.1', 3, 9987)
+ 'request_data' => self::getItems(1001, 1001, 'dependent.item.1', 3, $dep_count_overflow - 3)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level, combination.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.3" on the master item with key "dependent.item.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1002: dependent.items.template.1:dependent.item.1.1
// 1003: dependent.items.template.1:dependent.item.1.2
'request_data' => array_merge(
- self::getItems(1001, 1002, 'dependent.item.1.1', 3, 5495),
- self::getItems(1001, 1003, 'dependent.item.1.2', 3, 4494)
+ self::getItems(1001, 1002, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1001, 1003, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of discovery rule in the tree on the template level.
- [
+ 'Check for maximum count of discovery rule in the tree on the template level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, 9986)
+ 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2)
],
- // Check for maximum count of discovery rule in the tree on the template level.
- [
+ 'Check for maximum count of discovery rule in the tree on the template level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1002: dependent.items.template.1:dependent.item.1.1
// 1003: dependent.items.template.1:dependent.item.1.2
'request_data' => array_merge(
- self::getDiscoveryRule(1001, 1002, 'dependent.discovery.rule.1.1', 1, 5493),
- self::getDiscoveryRule(1001, 1003, 'dependent.discovery.rule.1.2', 1, 4492)
+ self::getDiscoveryRule(1001, 1002, 'dependent.discovery.rule.1.1', 1, floor($dep_count_overflow / 2) - 6),
+ self::getDiscoveryRule(1001, 1003, 'dependent.discovery.rule.1.2', 1, ceil($dep_count_overflow / 2))
)
],
- // Check for maximum count of items in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the host level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.3" on the master item with key "master.item.1" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1004: dependent.items.host.1
// 1301: dependent.items.host.1:master.item.1
- 'request_data' => self::getItems(1004, 1301, 'dependent.item.1', 3, 9987)
+ 'request_data' => self::getItems(1004, 1301, 'dependent.item.1', 3, $dep_count_overflow - 3 - 6)
],
- // Check for maximum count of items in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the host level, combination.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.3" on the master item with key "dependent.item.1.2" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1004: dependent.items.host.1
// 1302: dependent.items.host.1:dependent.item.1.1
// 1303: dependent.items.host.1:dependent.item.1.2
'request_data' => array_merge(
- self::getItems(1004, 1302, 'dependent.item.1.1', 3, 5495),
- self::getItems(1004, 1303, 'dependent.item.1.2', 3, 4494)
+ self::getItems(1004, 1302, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1004, 1303, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of discovery rule in the tree on the host level.
- [
+ 'Check for maximum count of discovery rule in the tree on the host level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1004: dependent.items.host.1
// 1301: dependent.items.host.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1004, 1301, 'dependent.discovery.rule.1', 2, 9986)
+ 'request_data' => self::getDiscoveryRule(1004, 1301, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2 - 6)
],
- [
+ 'Check for maximum count of discovery rule in the tree on the host level 2.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1004: dependent.items.host.1
// 1302: dependent.items.host.1:dependent.item.1.1
// 1303: dependent.items.host.1:dependent.item.1.2
'request_data' => array_merge(
- self::getDiscoveryRule(1004, 1302, 'dependent.discovery.rule.1.1', 1, 5493),
- self::getDiscoveryRule(1004, 1303, 'dependent.discovery.rule.1.2', 1, 4492)
+ self::getDiscoveryRule(1004, 1302, 'dependent.discovery.rule.1.1', 1, floor($dep_count_overflow / 2) - 6),
+ self::getDiscoveryRule(1004, 1303, 'dependent.discovery.rule.1.2', 1, ceil($dep_count_overflow / 2))
)
],
- // Check for maximum count of items in the tree on the template level.
- [
+ 'Check for maximum count of items in the tree on the template level, fill to max.' => [
'error' => null,
'method' => 'item.create',
// 1001: dependent.items.template.1
- // 1002: dependent.items.template.1:dependent.item.1.1
- // 1003: dependent.items.template.1:dependent.item.1.2
+ // 1002: dependent.items.template.1:dependent.item.1.1 (2 dependents)
+ // 1003: dependent.items.template.1:dependent.item.1.2 (2 dependents)
'request_data' => array_merge(
- self::getItems(1001, 1002, 'dependent.item.1.1', 3, 9494),
- self::getItems(1001, 1003, 'dependent.item.1.2', 3, 494)
+ self::getItems(1001, 1002, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1001, 1003, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3 - 6 /* 4 existing dependents + parent dependent items */)
)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level, adding overflow via 2nd item.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.30" on the master item with key "dependent.item.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1003: dependent.items.template.1:dependent.item.1.2
- 'request_data' => array_merge(self::getItems(1001, 1003, 'dependent.item.1.2', 495, 9495))
+ 'request_data' => self::getItems(1001, 1003, 'dependent.item.1.2', ceil($dep_count_overflow), ceil($dep_count_overflow) + 1)
],
- [
+ 'Check for maximum count of items in the tree on the template level, master item.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, 2)
+ 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2 - 6)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.3[{#LLD}]" on the master item prototype with key "master.item.proto.1" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1018: dependent.items.template.1:master.item.proto.1
- 'request_data' => self::getItemPrototypes(1001, 1017, 1018, 'dependent.item.proto.1', 3, 9988)
+ 'request_data' => self::getItemPrototypes(1001, 1017, 1018, 'dependent.item.proto.1', 3, $dep_count_overflow - 3)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template level, combination, fail.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.3[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1019: dependent.items.template.1:dependent.item.proto.1.1
// 1020: dependent.items.template.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, 4495)
+ self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of item prototypes in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the host level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.3[{#LLD}]" on the master item prototype with key "master.item.proto.1" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1004: dependent.items.host.1
// 1317: dependent.items.template.1:discovery.rule.1
// 1318: dependent.items.host.1:master.item.proto.1
- 'request_data' => self::getItemPrototypes(1004, 1317, 1318, 'dependent.item.proto.1', 3, 9988)
+ 'request_data' => self::getItemPrototypes(1004, 1317, 1318, 'dependent.item.proto.1', 3, $dep_count_overflow - 3)
],
- // Check for maximum count of item prototypes in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the host level, combination.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.3[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1004: dependent.items.host.1
// 1317: dependent.items.template.1:discovery.rule.1
// 1319: dependent.items.host.1:dependent.item.proto.1.1
// 1320: dependent.items.host.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1004, 1317, 1319, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1004, 1317, 1320, 'dependent.item.proto.1.2', 3, 4495)
+ self::getItemPrototypes(1004, 1317, 1319, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItemPrototypes(1004, 1317, 1320, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
+ 'Check for maximum count of item prototypes in the tree on the template level, combination, success.' => [
'error' => null,
'method' => 'itemprototype.create',
- // 1001: dependent.items.template.1
+ // 1001: dependent.items.template.1 (max occupancy checked here)
// 1017: dependent.items.template.1:discovery.rule.1
- // 1019: dependent.items.template.1:dependent.item.proto.1.1
- // 1020: dependent.items.template.1:dependent.item.proto.1.2
+ // 1019: dependent.items.template.1:dependent.item.proto.1.1 (2 dependents)
+ // 1020: dependent.items.template.1:dependent.item.proto.1.2 (2 dependents)
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, 4494)
+ self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3 - 2 /* dependents */),
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3 - 2 /* dependents */ - 1 /*rule itself*/)
)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.11[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1020: dependent.items.template.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 4495, 5494)
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', floor($dep_count_overflow / 2) - 5 + 1 /* from last above */, floor($dep_count_overflow / 2) - 4 + 1)
)
]
];
}
+
/**
* @dataProvider getTestCases
*/
- public function testDependentItems_Update($error, $method, $request_data) {
- $this->call($method, $request_data, $error);
+ public function testDependentItems_main($expected_error, $method, $request_data) {
+ static $reg_child_number = '/("dependent[^"]+\.)(\d*)([^"]*")/';
+
+ if ($expected_error === null || strrpos($expected_error, 'allowed count of dependent') === false) {
+ return $this->call($method, $request_data, $expected_error);
+ }
+
+ if (CAPIHelper::getSessionId() === null) {
+ $this->authorize(PHPUNIT_LOGIN_NAME, PHPUNIT_LOGIN_PWD);
+ }
+
+ $response = CAPIHelper::call($method, $request_data);
+
+ $this->assertArrayNotHasKey('result', $response);
+ $this->assertArrayHasKey('error', $response);
+
+ /*
+ To allow for varying ZBX_DEPENDENT_ITEM_MAX_COUNT, replace specific dependent "child" numbers, e.g.
+ dependent.item.proto.1.2.46[{#LLD}] becomes
+ dependent.item.proto.1.2.x[{#LLD}].
+ */
+ $expected_error = preg_replace($reg_child_number, '$1x$3', $expected_error);
+ $received_error = $response['error']['data'];
+
+ if (strrpos($received_error, 'allowed count of dependent') !== false) {
+ $received_error = preg_replace($reg_child_number, '$1x$3', $received_error);
+ }
+
+ $this->assertSame($expected_error, $received_error);
}
}
diff --git a/ui/tests/api_json/testIconMap.php b/ui/tests/api_json/testIconMap.php
index fc9ec09d4f3..5c61f3667eb 100644
--- a/ui/tests/api_json/testIconMap.php
+++ b/ui/tests/api_json/testIconMap.php
@@ -466,21 +466,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
- 'default_iconid' => '2',
- 'mappings' =>[
- [
- 'inventory_link' => '2',
- 'expression' => '\//',
- 'iconid' => '1'
- ]
- ]
- ],
- 'expected_error' => 'Invalid parameter "/1/mappings/1/expression": invalid regular expression.'
- ]],
- [[
- 'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 1',
'default_iconid' => '2',
'mappings' =>[
[
@@ -494,7 +480,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 2',
'default_iconid' => '2',
'mappings' =>[
[
@@ -508,7 +494,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 3',
'default_iconid' => '2',
'mappings' =>[
[
diff --git a/ui/tests/api_json/testItem.php b/ui/tests/api_json/testItem.php
index 28eb5073f54..f6a0699c3cb 100644
--- a/ui/tests/api_json/testItem.php
+++ b/ui/tests/api_json/testItem.php
@@ -47,62 +47,93 @@ class testItem extends CAPITest {
ITEM_TYPE_DEPENDENT => null,
ITEM_TYPE_HTTPAGENT => '50022',
ITEM_TYPE_SNMP => '50029',
- ITEM_TYPE_SCRIPT => '50022'
+ ITEM_TYPE_SCRIPT => null
];
-
$item_type_tests = [];
+
foreach ($valid_item_types as $type => $interfaceid) {
switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_INTERNAL:
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ case ITEM_TYPE_EXTERNAL:
+ $params = [
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DB_MONITOR:
+ $params = [
+ 'params' => 'SELECT * FROM table',
+ 'delay' => '30s'
+ ];
+ break;
+
case ITEM_TYPE_IPMI:
$params = [
- 'ipmi_sensor' => '1.2.3'
+ 'ipmi_sensor' => '1.2.3',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_TRAPPER:
+ case ITEM_TYPE_SSH:
$params = [
- 'delay' => '0'
+ 'username' => 'username',
+ 'authtype' => ITEM_AUTHTYPE_PASSWORD,
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_TELNET:
- case ITEM_TYPE_SSH:
$params = [
'username' => 'username',
- 'authtype' => ITEM_AUTHTYPE_PASSWORD
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_DEPENDENT:
+ case ITEM_TYPE_CALCULATED:
$params = [
- 'master_itemid' => '150151',
- 'delay' => '0'
+ 'params' => '1+1',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_JMX:
$params = [
'username' => 'username',
- 'password' => 'password'
+ 'password' => 'password',
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DEPENDENT:
+ $params = [
+ 'master_itemid' => '150151'
];
break;
case ITEM_TYPE_HTTPAGENT:
$params = [
- 'url' => 'http://0.0.0.0'
+ 'url' => 'http://0.0.0.0',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SNMP:
$params = [
- 'snmp_oid' => '1.2.3'
+ 'snmp_oid' => '1.2.3',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SCRIPT:
$params = [
'params' => 'script',
- 'timeout' => '30s'
+ 'timeout' => '30s',
+ 'delay' => '30s'
];
break;
@@ -121,8 +152,7 @@ class testItem extends CAPITest {
'key_' => 'item_of_type_'.$type,
'hostid' => '50009',
'type' => (string) $type,
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'delay' => '30s'
+ 'value_type' => ITEM_VALUE_TYPE_UINT64
],
'expected_error' => null
];
@@ -135,16 +165,30 @@ class testItem extends CAPITest {
foreach ($item_type_tests as $item_type_test) {
if (in_array($item_type_test['request_data']['type'], $optional)) {
unset($item_type_test['request_data']['interfaceid']);
- $interfaces_tests[] = $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' missing',
+ 'key_' => $item_type_test['request_data']['key_'].'_missing'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' zero',
+ 'key_' => $item_type_test['request_data']['key_'].'_zero',
+ 'interfaceid' => '0'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
}
else if (in_array($item_type_test['request_data']['type'], $required)) {
unset($item_type_test['request_data']['interfaceid']);
- $item_type_test['expected_error'] = 'No interface found.';
+ $item_type_test['expected_error'] = 'Invalid parameter "/1": the parameter "interfaceid" is missing.';
$interfaces_tests[] = $item_type_test;
}
}
- return [
+ return array_merge([
[
'request_data' => [
'hostid' => '50009',
@@ -184,7 +228,7 @@ class testItem extends CAPITest {
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX
],
- 'expected_error' => 'Incorrect arguments passed to function.'
+ 'expected_error' => 'Invalid parameter "/1": the parameter "delay" is missing.'
],
[
'request_data' => [
@@ -196,7 +240,7 @@ class testItem extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for mqtt key of the Active agent type.
[
@@ -204,7 +248,6 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt key for active agent',
'key_' => 'mqtt.get[3]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
@@ -215,10 +258,8 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt key with 0 delay for active agent',
'key_' => 'mqtt.get[4]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0'
+ 'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
'expected_error' => null
],
@@ -229,7 +270,6 @@ class testItem extends CAPITest {
'key_' => 'trapper_item_1',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_TRAPPER,
- 'delay' => '0',
'tags' => [
[
'tag' => 'tag',
@@ -248,12 +288,11 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt with wrong key and 0 delay',
'key_' => 'mqt.get[5]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Item preprocessing.
[
@@ -264,19 +303,17 @@ class testItem extends CAPITest {
'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0',
'preprocessing' => [
[
'type' => ZBX_PREPROC_VALIDATE_NOT_SUPPORTED,
- 'params' => '',
'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
'error_handler_params' => ''
]
]
],
- 'expected_error' => 'Incorrect value for field "error_handler": unexpected value "0".'
+ 'expected_error' => 'Invalid parameter "/1/preprocessing/1/error_handler": value must be one of 1, 2, 3.'
]
- ] + $item_type_tests + $interfaces_tests;
+ ], $item_type_tests, $interfaces_tests);
}
/**
@@ -290,6 +327,10 @@ class testItem extends CAPITest {
$request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
}
+ if (!array_key_exists('delay', $request_data)) {
+ $request_data['delay'] = 0;
+ }
+
foreach ($result['result']['itemids'] as $id) {
$db_item = CDBHelper::getRow('SELECT hostid, name, key_, type, delay FROM items WHERE itemid='.zbx_dbstr($id));
@@ -366,7 +407,7 @@ class testItem extends CAPITest {
'key_' => 'mqtt.get[00]',
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for wrong mqtt key of the Active agent item type.
[
@@ -376,15 +417,14 @@ class testItem extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Change type to active agent and check update interval for mqtt key.
[
'request_data' => [
'item' => 'testItem_Update:agent.ping',
'key_' => 'mqtt.get[22]',
- 'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0'
+ 'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
'expected_error' => null
],
@@ -411,6 +451,7 @@ class testItem extends CAPITest {
if ($expected_error === null) {
if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
+
$request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
}
diff --git a/ui/tests/api_json/testItemPrototype.php b/ui/tests/api_json/testItemPrototype.php
index 1332ab75ed8..3315bf649ed 100644
--- a/ui/tests/api_json/testItemPrototype.php
+++ b/ui/tests/api_json/testItemPrototype.php
@@ -43,62 +43,93 @@ class testItemPrototype extends CAPITest {
ITEM_TYPE_DEPENDENT => null,
ITEM_TYPE_HTTPAGENT => '50022',
ITEM_TYPE_SNMP => '50029',
- ITEM_TYPE_SCRIPT => '50022'
+ ITEM_TYPE_SCRIPT => null
];
$item_type_tests = [];
foreach ($valid_item_types as $type => $interfaceid) {
switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_INTERNAL:
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ case ITEM_TYPE_EXTERNAL:
+ $params = [
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DB_MONITOR:
+ $params = [
+ 'params' => 'SELECT * FROM table',
+ 'delay' => '30s'
+ ];
+ break;
+
case ITEM_TYPE_IPMI:
$params = [
- 'ipmi_sensor' => '1.2.3'
+ 'ipmi_sensor' => '1.2.3',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_TRAPPER:
+ case ITEM_TYPE_SSH:
$params = [
- 'delay' => '0'
+ 'username' => 'username',
+ 'authtype' => ITEM_AUTHTYPE_PASSWORD,
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_TELNET:
- case ITEM_TYPE_SSH:
$params = [
'username' => 'username',
- 'authtype' => ITEM_AUTHTYPE_PASSWORD
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_DEPENDENT:
+ case ITEM_TYPE_CALCULATED:
$params = [
- 'master_itemid' => '150151',
- 'delay' => '0'
+ 'params' => '1+1',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_JMX:
$params = [
'username' => 'username',
- 'password' => 'password'
+ 'password' => 'password',
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DEPENDENT:
+ $params = [
+ 'master_itemid' => '150151'
];
break;
case ITEM_TYPE_HTTPAGENT:
$params = [
- 'url' => 'http://0.0.0.0'
+ 'url' => 'http://0.0.0.0',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SNMP:
$params = [
- 'snmp_oid' => '1.2.3'
+ 'snmp_oid' => '1.2.3',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SCRIPT:
$params = [
'params' => 'script',
- 'timeout' => '30s'
+ 'timeout' => '30s',
+ 'delay' => '30s'
];
break;
@@ -116,16 +147,45 @@ class testItemPrototype extends CAPITest {
'hostid' => '50009',
'ruleid' => '400660',
'name' => 'Test item prototype of type '.$type,
- 'key_' => 'test_item_prototype_of_type_'.$type,
+ 'key_' => 'test_item_prototype_of_type_'.$type.'[{#LLD_MACRO}]',
'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'type' => (string) $type,
- 'delay' => '30s'
+ 'type' => (string) $type
],
'expected_error' => null
];
}
- return [
+ $interfaces_tests = [];
+ $optional = [ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_HTTPAGENT];
+ $required = [ITEM_TYPE_SNMP, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_IPMI, ITEM_TYPE_ZABBIX, ITEM_TYPE_JMX];
+
+ foreach ($item_type_tests as $item_type_test) {
+ if (in_array($item_type_test['request_data']['type'], $optional)) {
+ unset($item_type_test['request_data']['interfaceid']);
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' missing',
+ 'key_' => substr($item_type_test['request_data']['key_'], 0, -1).', missing]'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' zero',
+ 'key_' => substr($item_type_test['request_data']['key_'], 0, -1).', zero]',
+ 'interfaceid' => '0'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+ }
+ else if (in_array($item_type_test['request_data']['type'], $required)) {
+ unset($item_type_test['request_data']['interfaceid']);
+ $item_type_test['expected_error'] = 'Invalid parameter "/1": the parameter "interfaceid" is missing.';
+ $interfaces_tests[] = $item_type_test;
+ }
+ }
+
+ return array_merge([
[
'request_data' => [
'hostid' => '50009',
@@ -167,7 +227,7 @@ class testItemPrototype extends CAPITest {
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX
],
- 'expected_error' => 'Incorrect arguments passed to function.'
+ 'expected_error' => 'Invalid parameter "/1": the parameter "ruleid" is missing.'
],
[
'request_data' => [
@@ -180,7 +240,7 @@ class testItemPrototype extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for mqtt key of the Active agent type.
[
@@ -189,7 +249,6 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt key for active agent',
'key_' => 'mqtt.get[{#3}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
@@ -201,7 +260,6 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt key with 0 delay for active agent',
'key_' => 'mqtt.get[{#4}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
@@ -214,14 +272,13 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt with wrong key and 0 delay',
'key_' => 'mqt.get[{#5}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
- ] + $item_type_tests;
+ ], $item_type_tests, $interfaces_tests);
}
/**
@@ -231,9 +288,13 @@ class testItemPrototype extends CAPITest {
$result = $this->call('itemprototype.create', $request_data, $expected_error);
if ($expected_error === null) {
- if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
- $request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
- }
+ if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
+ $request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
+ }
+
+ if (!array_key_exists('delay', $request_data)) {
+ $request_data['delay'] = 0;
+ }
foreach ($result['result']['itemids'] as $id) {
$db_item = CDBHelper::getRow('SELECT hostid, name, key_, type, delay FROM items WHERE itemid='.zbx_dbstr($id));
diff --git a/ui/tests/api_json/testWebScenario.php b/ui/tests/api_json/testWebScenario.php
index f0524bc84f8..2fda9c0da3c 100644
--- a/ui/tests/api_json/testWebScenario.php
+++ b/ui/tests/api_json/testWebScenario.php
@@ -1588,12 +1588,6 @@ class testWebScenario extends CAPITest {
]
]
], null);
-
- $itemids = array_keys(array_fill(150151, 9, 0));
-
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webtest key_name"').', '.zbx_dbstr('"$1"').') WHERE '.dbConditionInt('itemid', $itemids));
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webstep name 1"').', '.zbx_dbstr('"$2"').') WHERE '.dbConditionInt('itemid', $itemids));
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webstep name 2"').', '.zbx_dbstr('"$2"').') WHERE '.dbConditionInt('itemid', $itemids));
}
/**
diff --git a/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml b/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
index b18e006e3e3..ad91665d7b9 100644
--- a/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
+++ b/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
@@ -325,7 +325,7 @@
<type>2</type>
<snmp_community/>
<snmp_oid/>
- <key>12348</key>
+ <key>12348[{#LLD_MACRO}]</key>
<delay>0</delay>
<history>90d</history>
<trends>365d</trends>
@@ -385,7 +385,7 @@
<type>2</type>
<snmp_community/>
<snmp_oid/>
- <key>12349</key>
+ <key>12349[{#LLD_MACRO}]</key>
<delay>0</delay>
<history>90d</history>
<trends>365d</trends>
@@ -443,7 +443,7 @@
</item_prototypes>
<trigger_prototypes>
<trigger_prototype>
- <expression>{12345:12348.last()}</expression>
+ <expression>{12345:12348[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12348</name>
@@ -459,7 +459,7 @@
<tags/>
</trigger_prototype>
<trigger_prototype>
- <expression>{12345:12348.last()} or {12345:12349.last()}</expression>
+ <expression>{12345:12348[{#LLD_MACRO}].last()} or {12345:12349[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12348</name>
@@ -475,7 +475,7 @@
<tags/>
</trigger_prototype>
<trigger_prototype>
- <expression>{12345:12349.last()}</expression>
+ <expression>{12345:12349[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12349</name>
@@ -519,7 +519,7 @@
<type>0</type>
<item>
<host>12345</host>
- <key>12348</key>
+ <key>12348[{#LLD_MACRO}]</key>
</item>
</graph_item>
<graph_item>
@@ -531,7 +531,7 @@
<type>0</type>
<item>
<host>12345</host>
- <key>12349</key>
+ <key>12349[{#LLD_MACRO}]</key>
</item>
</graph_item>
</graph_items>
diff --git a/ui/tests/include/helpers/CDataHelper.php b/ui/tests/include/helpers/CDataHelper.php
index 0202c8cf031..c6c25eb5896 100644
--- a/ui/tests/include/helpers/CDataHelper.php
+++ b/ui/tests/include/helpers/CDataHelper.php
@@ -226,8 +226,11 @@ class CDataHelper extends CAPIHelper {
if (array_key_exists($host, $interfaces['default_interfaces'])) {
$interface_type = null;
switch (CTestArrayHelper::get($item, 'type')) {
- case ITEM_TYPE_ZABBIX:
case ITEM_TYPE_ZABBIX_ACTIVE:
+ $interface_type = 0;
+ break;
+
+ case ITEM_TYPE_ZABBIX:
$interface_type = 1;
break;
diff --git a/ui/tests/integration/data/confsync_hosts.xml b/ui/tests/integration/data/confsync_hosts.xml
index 76642623235..568275d600f 100644
--- a/ui/tests/integration/data/confsync_hosts.xml
+++ b/ui/tests/integration/data/confsync_hosts.xml
@@ -191,6 +191,7 @@
<name>Hostname</name>
<key>agent.hostname</key>
<trends>0</trends>
+ <delay>15s</delay>
<value_type>CHAR</value_type>
<inventory_link>NAME</inventory_link>
<interface_ref>if1</interface_ref>
@@ -271,11 +272,11 @@
<item_prototype>
<name>ItemProto</name>
<type>CALCULATED</type>
- <key>proto</key>
+ <key>proto[{#X}]</key>
<params>1</params>
<trigger_prototypes>
<trigger_prototype>
- <expression>last(/HostWithDiscovery/proto)=1</expression>
+ <expression>last(/HostWithDiscovery/proto[{#X}])=1</expression>
<name>TriggerPrototype</name>
<priority>DISASTER</priority>
<manual_close>YES</manual_close>
diff --git a/ui/tests/integration/data/confsync_hosts_updated.xml b/ui/tests/integration/data/confsync_hosts_updated.xml
index faf353b00dc..3382bd9071c 100644
--- a/ui/tests/integration/data/confsync_hosts_updated.xml
+++ b/ui/tests/integration/data/confsync_hosts_updated.xml
@@ -188,7 +188,7 @@
</interfaces>
<items>
<item>
- <name>Hostname</name>
+ <name>Hostname1</name>
<key>agent.hostname</key>
<trends>0</trends>
<delay>25s</delay>
@@ -197,7 +197,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Chassis</name>
+ <name>Chassis1</name>
<key>system.hw.chassis[model]</key>
<trends>0</trends>
<delay>25s</delay>
@@ -206,7 +206,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Uname</name>
+ <name>Uname1</name>
<key>system.uname</key>
<trends>0</trends>
<delay>25s</delay>
@@ -238,9 +238,9 @@
</interfaces>
<items>
<item>
- <name>ItemWithMultilevelMacros</name>
- <key>vfs.dir.count[/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
- <delay>{$PRNTTMPLMACRO}</delay>
+ <name>ItemWithMultilevelMacros1</name>
+ <key>vfs.dir.count[/tmp/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
+ <delay>{$CHLDTMPLMACRO}</delay>
<interface_ref>if1</interface_ref>
</item>
</items>
@@ -272,13 +272,13 @@
</filter>
<item_prototypes>
<item_prototype>
- <name>ItemProto</name>
+ <name>ItemProtoo</name>
<type>CALCULATED</type>
- <key>proto</key>
+ <key>proto[{#X}]</key>
<params>1</params>
<trigger_prototypes>
<trigger_prototype>
- <expression>last(/HostWithDiscovery/proto)=2</expression>
+ <expression>last(/HostWithDiscovery/proto[{#X}])=2</expression>
<name>TriggerPrototype</name>
<priority>HIGH</priority>
<manual_close>YES</manual_close>
@@ -371,7 +371,7 @@
</interfaces>
<items>
<item>
- <name>NormalItemPreproc</name>
+ <name>NormalItemPreproc1</name>
<key>agent.hostmetadata</key>
<trends>0</trends>
<delay>20s</delay>
@@ -388,7 +388,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Agent ping (active)</name>
+ <name>Agent ping (active)1</name>
<type>ZABBIX_ACTIVE</type>
<key>agent.ping</key>
<delay>20s</delay>
@@ -408,7 +408,7 @@
</triggers>
</item>
<item>
- <name>Agent version (passive)</name>
+ <name>Agent version (passive)1</name>
<key>agent.version</key>
<delay>20s</delay>
<trends>0</trends>
@@ -416,7 +416,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Calculated</name>
+ <name>Calculated1</name>
<type>CALCULATED</type>
<key>calc</key>
<params>1+1+1</params>
@@ -465,13 +465,13 @@
</triggers>
</item>
<item>
- <name>CalculatedAsAggregated</name>
+ <name>CalculatedAsAggregated1b</name>
<type>CALCULATED</type>
<key>calc_as_aggr</key>
<params>sum(last_foreach(/*/agent.version?[group=&quot;HG1&quot;]))</params>
</item>
<item>
- <name>CalculatedPreproc</name>
+ <name>CalculatedPreproc1b</name>
<type>CALCULATED</type>
<key>calc_preproc</key>
<params>1</params>
@@ -486,25 +486,25 @@
</preprocessing>
</item>
<item>
- <name>ODBC monitor</name>
+ <name>ODBC monitor1</name>
<type>ODBC</type>
<key>db.odbc.select[x,y,z]</key>
<params>select null from dual;</params>
</item>
<item>
- <name>Dependent item</name>
+ <name>Dependent itemm</name>
<type>DEPENDENT</type>
<key>depitem</key>
- <delay>10s</delay>
+ <delay>0</delay>
<master_item>
<key>calc</key>
</master_item>
</item>
<item>
- <name>DepItemOnCalcPreproc</name>
+ <name>DepItemOnCalcPreprocc</name>
<type>DEPENDENT</type>
<key>depitem_calc_preproc</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -519,10 +519,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnDepCalcItem</name>
+ <name>DepItemOnDepCalcItemm</name>
<type>DEPENDENT</type>
<key>depitem_depitem</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -537,10 +537,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnNormalDepItem</name>
+ <name>DepItemOnNormalDepItemm</name>
<type>DEPENDENT</type>
<key>depitem_depitem_normal</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -555,15 +555,15 @@
</master_item>
</item>
<item>
- <name>Zabbix internal</name>
+ <name>Zabbix internall</name>
<type>INTERNAL</type>
<key>zabbix[boottime]</key>
</item>
<item>
- <name>DepItemOnInternalItem</name>
+ <name>DepItemOnInternalItemm</name>
<type>DEPENDENT</type>
<key>depitem_internal</key>
- <delay>30s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -578,10 +578,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnNormalItem</name>
+ <name>DepItemOnNormalItemm</name>
<type>DEPENDENT</type>
<key>depitem_normalitem</key>
- <delay>30s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>REGEX</type>
@@ -596,10 +596,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnInternalItemPreproc</name>
+ <name>DepItemOnInternalItemPreprocc</name>
<type>DEPENDENT</type>
<key>depitem_normal_preproc</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -614,13 +614,13 @@
</master_item>
</item>
<item>
- <name>External check</name>
+ <name>External checkk</name>
<type>EXTERNAL</type>
<key>extcheck</key>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>HTTP agent</name>
+ <name>HTTP agentt</name>
<type>HTTP_AGENT</type>
<key>httpagent</key>
<authtype>BASIC</authtype>
@@ -646,22 +646,22 @@
<allow_traps>YES</allow_traps>
</item>
<item>
- <name>Simple check</name>
+ <name>Simple checkk</name>
<type>SIMPLE</type>
<key>icmpping[127.1.0.1]</key>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>IPMI agent</name>
+ <name>IPMI agentt</name>
<type>IPMI</type>
<key>ipmi.get</key>
<trends>0</trends>
<value_type>TEXT</value_type>
- <ipmi_sensor>1234</ipmi_sensor>
+ <ipmi_sensor>12134</ipmi_sensor>
<interface_ref>if3</interface_ref>
</item>
<item>
- <name>ItemPreproc</name>
+ <name>ItemPreprocc</name>
<type>CALCULATED</type>
<key>preproc</key>
<params>1</params>
@@ -678,7 +678,7 @@
</preprocessing>
</item>
<item>
- <name>Script</name>
+ <name>Scripyt</name>
<type>SCRIPT</type>
<key>script</key>
<params>return &quot;1&quot;;</params>
@@ -691,21 +691,21 @@
</parameters>
</item>
<item>
- <name>SNMP trap</name>
+ <name>SNMP trapq</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
- <delay>10s</delay>
+ <delay>0</delay>
<interface_ref>if2</interface_ref>
</item>
<item>
- <name>SNMP agent</name>
+ <name>SNMP agentt</name>
<type>SNMP_AGENT</type>
<snmp_oid>SNMPv2-MIB::sysContact.0</snmp_oid>
<key>snmp_test</key>
<interface_ref>if2</interface_ref>
</item>
<item>
- <name>SSH agent</name>
+ <name>SSH agentt</name>
<type>SSH</type>
<key>ssh.run[x,localhost]</key>
<params>echo</params>
@@ -714,7 +714,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Telnet agent</name>
+ <name>Telnet agentt</name>
<type>TELNET</type>
<key>telnet.run[a,localhost]</key>
<params>echo</params>
@@ -722,14 +722,15 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Zabbix trapper</name>
+ <name>Zabbix trappero</name>
<type>TRAP</type>
<key>trap</key>
- <delay>10s</delay>
+ <delay>0</delay>
+ <units>MB</units>
</item>
<item>
- <name>delay_globmacro</name>
- <key>vfs.file.get[{$UU}]</key>
+ <name>delay_globmacro1</name>
+ <key>vfs.file.get[/tmp/{$UU}]</key>
<delay>{$GLOBDELAY}</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
@@ -754,7 +755,7 @@
</interfaces>
<items>
<item>
- <name>item1</name>
+ <name>item11</name>
<type>CALCULATED</type>
<key>itm</key>
<trends>0</trends>
@@ -769,15 +770,16 @@
</triggers>
</item>
<item>
- <name>ItemWithGlobalAndHostMacro</name>
- <key>sensor[{$HM1},{$UU}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <name>ItemWithGlobalAndHostMacro1</name>
+ <key>sensor[{$UU},{$HM1}]</key>
+ <delay>1s</delay>
+ <units>MB</units>
<value_type>FLOAT</value_type>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>ItemWithMacro</name>
- <key>vfs.file.get[/{$HM1}]</key>
+ <name>ItemWithMacro1</name>
+ <key>vfs.file.get[/tmp/{$HM1}]</key>
<trends>0</trends>
<value_type>TEXT</value_type>
<interface_ref>if1</interface_ref>
@@ -846,10 +848,11 @@
</groups>
<items>
<item>
- <name>DepItemOnWebScenario</name>
+ <name>DepItemOnWebScenarioo</name>
<type>DEPENDENT</type>
<key>dep_webscenario</key>
- <delay>20s</delay>
+ <delay>0</delay>
+ <units>MB</units>
<master_item>
<key>web.test.in[WebScenario,,bps]</key>
</master_item>
diff --git a/ui/tests/integration/data/confsync_tmpl.xml b/ui/tests/integration/data/confsync_tmpl.xml
index fdc9f194fcc..8fe30e4e2a1 100644
--- a/ui/tests/integration/data/confsync_tmpl.xml
+++ b/ui/tests/integration/data/confsync_tmpl.xml
@@ -83,7 +83,7 @@
<uuid>f988e57fcdc541668c3c32919d3cfa60</uuid>
<name>DepItemProtoDependsOnNormalItem</name>
<type>DEPENDENT</type>
- <key>depitemproto_deponnormal</key>
+ <key>depitemproto_deponnormal[{#LLDMACRO}]</key>
<delay>0</delay>
<master_item>
<key>tmpltrap</key>
@@ -91,12 +91,12 @@
<trigger_prototypes>
<trigger_prototype>
<uuid>ebc83aff113846518d3e1c88532274e1</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=0</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=0</expression>
<name>proto_dep {#LLDMACRO}</name>
<dependencies>
<dependency>
<name>proto {#LLDMACRO}</name>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=1</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=1</expression>
</dependency>
</dependencies>
<tags>
@@ -108,7 +108,7 @@
</trigger_prototype>
<trigger_prototype>
<uuid>6032b6444698467c8f41587858f089e5</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=1</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=1</expression>
<name>proto {#LLDMACRO}</name>
<tags>
<tag>
@@ -592,7 +592,7 @@
<uuid>d94160c34be4455bba660b1b18c0fb0e</uuid>
<name>ItemWithGlobalAndHostMacro1</name>
<key>sensor[{$UU},{$HM1}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <delay>11s</delay>
<value_type>FLOAT</value_type>
</item>
<item>
@@ -600,7 +600,7 @@
<name>SNMP trap1</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
- <delay>10s</delay>
+ <delay>0</delay>
</item>
<item>
<uuid>0106d4d9fba64066a66fbfe2d2340602</uuid>
@@ -659,6 +659,7 @@
<uuid>381db19a280a4c27a1ad86693ea535bc</uuid>
<name>ItemWithMacro1</name>
<key>vfs.file.get[/tmp/{$HM1}]</key>
+ <delay>2s</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
</item>
diff --git a/ui/tests/integration/data/confsync_tmpl_updated.xml b/ui/tests/integration/data/confsync_tmpl_updated.xml
index e6a62ecf22c..94491db8cda 100644
--- a/ui/tests/integration/data/confsync_tmpl_updated.xml
+++ b/ui/tests/integration/data/confsync_tmpl_updated.xml
@@ -81,22 +81,22 @@
<item_prototypes>
<item_prototype>
<uuid>4d34d26d2480480c862261b1b9e9d99d</uuid>
- <name>DepItemProtoDependsOnNormalItem</name>
+ <name>DepItemProtoDependsOnNormalItem1</name>
<type>DEPENDENT</type>
<key>depitemproto_deponnormal</key>
- <delay>10s</delay>
+ <delay>0</delay>
<master_item>
<key>tmpltrap</key>
</master_item>
<trigger_prototypes>
<trigger_prototype>
<uuid>c64594a285b64df79b137eeaec10e52b</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=10</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=10</expression>
<name>proto_dep {#LLDMACRO}</name>
<dependencies>
<dependency>
<name>proto {#LLDMACRO}</name>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=110</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=110</expression>
</dependency>
</dependencies>
<tags>
@@ -108,7 +108,7 @@
</trigger_prototype>
<trigger_prototype>
<uuid>1f2fbda374544b0cb77869c6f1d88314</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=10</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=10</expression>
<name>proto {#LLDMACRO}</name>
<tags>
<tag>
@@ -179,7 +179,7 @@
<items>
<item>
<uuid>9a1082526795476ab429acd9caafc920</uuid>
- <name>NormalItemPreproc1</name>
+ <name>NormalItemPreproc11</name>
<key>agent.hostmetadata</key>
<delay>23s</delay>
<trends>0</trends>
@@ -196,7 +196,7 @@
</item>
<item>
<uuid>79a3b0eca3764c64abc7c53b83c0ce83</uuid>
- <name>Hostname1</name>
+ <name>Hostname11</name>
<key>agent.hostname</key>
<delay>23s</delay>
<trends>0</trends>
@@ -285,13 +285,13 @@
</item>
<item>
<uuid>16e297773b8d48958ebd4da74918687c</uuid>
- <name>CalculatedItemPreprocStepTest1</name>
+ <name>CalculatedItemPreprocStepTest11</name>
<type>CALCULATED</type>
<key>calculateditempreprocsteptest</key>
<delay>23s</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
- <params>1+1</params>
+ <params>1+1+1</params>
<preprocessing>
<step>
<type>REGEX</type>
@@ -349,7 +349,7 @@
</item>
<item>
<uuid>34693044a4b84f269f829c92b8e6888f</uuid>
- <name>CalculatedAsAggregated1</name>
+ <name>CalculatedAsAggregated11</name>
<type>CALCULATED</type>
<key>calc_as_aggr</key>
<delay>23s</delay>
@@ -357,7 +357,7 @@
</item>
<item>
<uuid>489114fec8b44645b96b9c463ea54f22</uuid>
- <name>CalculatedPreproc1</name>
+ <name>CalculatedPreproc11</name>
<type>CALCULATED</type>
<key>calc_preproc</key>
<delay>23s</delay>
@@ -366,15 +366,15 @@
<step>
<type>STR_REPLACE</type>
<parameters>
- <parameter>3</parameter>
- <parameter>4</parameter>
+ <parameter>31</parameter>
+ <parameter>41</parameter>
</parameters>
</step>
</preprocessing>
</item>
<item>
<uuid>de2c2812f00240ec84359fb3c0165ff6</uuid>
- <name>ODBC monitor1</name>
+ <name>ODBC monitor11</name>
<type>ODBC</type>
<key>db.odbc.select[x,y,z]</key>
<delay>23s</delay>
@@ -382,10 +382,11 @@
</item>
<item>
<uuid>2012d2c4562340d990eebfac680cc1da</uuid>
- <name>Dependent item1</name>
+ <name>Dependent item11</name>
<type>DEPENDENT</type>
<key>depitem</key>
<delay>0</delay>
+ <units>MB</units>
<inventory_link>TYPE</inventory_link>
<master_item>
<key>calc</key>
@@ -393,7 +394,7 @@
</item>
<item>
<uuid>d8def5fedd5a42a7b9a910c805a56899</uuid>
- <name>DepItemOnCalcPreproc1</name>
+ <name>DepItemOnCalcPreproc11</name>
<type>DEPENDENT</type>
<key>depitem_calc_preproc</key>
<delay>0</delay>
@@ -413,7 +414,7 @@
</item>
<item>
<uuid>10741348d10842d5a2888c7c3ebe4063</uuid>
- <name>DepItemOnDepCalcItem1</name>
+ <name>DepItemOnDepCalcItem11</name>
<type>DEPENDENT</type>
<key>depitem_depitem</key>
<delay>0</delay>
@@ -433,7 +434,7 @@
</item>
<item>
<uuid>d3e8a08190584da2a79f0c2c665a62ee</uuid>
- <name>DepItemOnNormalDepItem1</name>
+ <name>DepItemOnNormalDepItem11</name>
<type>DEPENDENT</type>
<key>depitem_depitem_normal</key>
<delay>0</delay>
@@ -453,7 +454,7 @@
</item>
<item>
<uuid>fd462776884943c08b6998bff03d5267</uuid>
- <name>DepItemOnInternalItem1</name>
+ <name>DepItemOnInternalItem11</name>
<type>DEPENDENT</type>
<key>depitem_internal</key>
<delay>0</delay>
@@ -473,7 +474,7 @@
</item>
<item>
<uuid>a1baaaa5e7af48578a749c5380426ed6</uuid>
- <name>DepItemOnNormalItem1</name>
+ <name>DepItemOnNormalItem11</name>
<type>DEPENDENT</type>
<key>depitem_normalitem</key>
<delay>0</delay>
@@ -493,7 +494,7 @@
</item>
<item>
<uuid>0a65fd6e155747ad9768e1c48c874264</uuid>
- <name>DepItemOnInternalItemPreproc1</name>
+ <name>DepItemOnInternalItemPreproc11</name>
<type>DEPENDENT</type>
<key>depitem_normal_preproc</key>
<delay>0</delay>
@@ -513,14 +514,14 @@
</item>
<item>
<uuid>e2b844f2d30440ef88deff5f3a596e1b</uuid>
- <name>External check1</name>
+ <name>External check11</name>
<type>EXTERNAL</type>
<key>extcheck</key>
<delay>23s</delay>
</item>
<item>
<uuid>2644c18c42864249a4be85871879af30</uuid>
- <name>HTTP agent1</name>
+ <name>HTTP agent11</name>
<type>HTTP_AGENT</type>
<key>httpagent</key>
<delay>23s</delay>
@@ -547,14 +548,14 @@
</item>
<item>
<uuid>352e9f1baba8491f9fcf4e0e8d968f4d</uuid>
- <name>Simple check1</name>
+ <name>Simple check11</name>
<type>SIMPLE</type>
<key>icmpping[127.0.0.1]</key>
<delay>23s</delay>
</item>
<item>
<uuid>f219f82b5f2f41d0a72f718684476638</uuid>
- <name>IPMI agent1</name>
+ <name>IPMI agent11</name>
<type>IPMI</type>
<key>ipmi.get</key>
<delay>23s</delay>
@@ -564,7 +565,7 @@
</item>
<item>
<uuid>5184862f271d4f8ebec44ef6526c1da1</uuid>
- <name>item11</name>
+ <name>item111</name>
<type>CALCULATED</type>
<key>itm</key>
<delay>23s</delay>
@@ -582,7 +583,7 @@
</item>
<item>
<uuid>3ce4f0ce210148cdb778a05e00bbdc81</uuid>
- <name>ItemPreproc1</name>
+ <name>ItemPreproc11</name>
<type>CALCULATED</type>
<key>preproc</key>
<delay>23s</delay>
@@ -601,7 +602,7 @@
</item>
<item>
<uuid>74db5c8a26b447468c2f2590af135878</uuid>
- <name>Script1</name>
+ <name>Script11</name>
<type>SCRIPT</type>
<key>script</key>
<delay>23s</delay>
@@ -616,14 +617,14 @@
</item>
<item>
<uuid>d94160c34be4455bba660b1b18c0fb0e</uuid>
- <name>ItemWithGlobalAndHostMacro1</name>
+ <name>ItemWithGlobalAndHostMacro11</name>
<key>sensor[{$UU},{$HM1}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <delay>12s</delay>
<value_type>FLOAT</value_type>
</item>
<item>
<uuid>5861cb498c044908a7aa5b225500ba3d</uuid>
- <name>SNMP trap1</name>
+ <name>SNMP trap11</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
<delay>0</delay>
@@ -631,7 +632,7 @@
</item>
<item>
<uuid>0106d4d9fba64066a66fbfe2d2340602</uuid>
- <name>SNMP agent1</name>
+ <name>SNMP agent11</name>
<type>SNMP_AGENT</type>
<snmp_oid>SNMPv2-MIB::sysContact.0</snmp_oid>
<key>snmp_test</key>
@@ -639,7 +640,7 @@
</item>
<item>
<uuid>49db5d3e8da84948a543801129a84b99</uuid>
- <name>SSH agent1</name>
+ <name>SSH agent11</name>
<type>SSH</type>
<key>ssh.run[x,localhost]</key>
<delay>23s</delay>
@@ -649,7 +650,7 @@
</item>
<item>
<uuid>74939b6a93294881ac5ec83d885920c9</uuid>
- <name>Chassis1</name>
+ <name>Chassis11</name>
<key>system.hw.chassis[model]</key>
<delay>23s</delay>
<trends>0</trends>
@@ -658,7 +659,7 @@
</item>
<item>
<uuid>8d311f6a4fb44a4ab325600539f8398e</uuid>
- <name>Uname1</name>
+ <name>Uname11</name>
<key>system.uname</key>
<delay>23s</delay>
<trends>0</trends>
@@ -667,7 +668,7 @@
</item>
<item>
<uuid>dca59a5058074e9f96a3d81f973bba75</uuid>
- <name>Telnet agent1</name>
+ <name>Telnet agent11</name>
<type>TELNET</type>
<key>telnet.run[a,localhost]</key>
<delay>23s</delay>
@@ -676,7 +677,7 @@
</item>
<item>
<uuid>aaaff64eee7e47589a6b6a329a1060a3</uuid>
- <name>Zabbix trapper1</name>
+ <name>Zabbix trapper11</name>
<type>TRAP</type>
<key>trap</key>
<delay>0</delay>
@@ -684,14 +685,14 @@
</item>
<item>
<uuid>a1dfb8d4a8004159979dff4f884f5d89</uuid>
- <name>ItemWithMultilevelMacros1</name>
- <key>vfs.dir.count[/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
+ <name>ItemWithMultilevelMacros11</name>
+ <key>vfs.dir.count[/tmp/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
<delay>{$PRNTTMPLMACRO}</delay>
<units>MB</units>
</item>
<item>
<uuid>381db19a280a4c27a1ad86693ea535bc</uuid>
- <name>ItemWithMacro1</name>
+ <name>ItemWithMacro11</name>
<key>vfs.file.get[/tmp/{$HM1}]</key>
<delay>23s</delay>
<trends>0</trends>
@@ -699,15 +700,15 @@
</item>
<item>
<uuid>ec8e28d9728f4418af7847db9ce17612</uuid>
- <name>delay_globmacro1</name>
- <key>vfs.file.get[/{$UU}]</key>
+ <name>delay_globmacro11</name>
+ <key>vfs.file.get[/tmp/{$UU}]</key>
<delay>{$GLOBDELAY}</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
</item>
<item>
<uuid>2a562bcf814343afb9c5cc4e9619e307</uuid>
- <name>Zabbix internal1</name>
+ <name>Zabbix internal11</name>
<type>INTERNAL</type>
<key>zabbix[boottime]</key>
<delay>23s</delay>
diff --git a/ui/tests/integration/testActiveAvailability.php b/ui/tests/integration/testActiveAvailability.php
index 9b8e919a4f2..b1bb805a535 100644
--- a/ui/tests/integration/testActiveAvailability.php
+++ b/ui/tests/integration/testActiveAvailability.php
@@ -154,7 +154,7 @@ class testActiveAvailability extends CIntegrationTest {
'hostid' => self::$hostid,
'name' => "act1",
'key_' => 'vfs.dir.size['.self::ACT_FILE_NAME.']',
- 'interfaceid' => self::$interfaceid,
+ 'interfaceid' => 0,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'value_type' => ITEM_VALUE_TYPE_TEXT,
'delay' => '1s'
diff --git a/ui/tests/integration/testGoAgentDataCollection.php b/ui/tests/integration/testGoAgentDataCollection.php
index 9ea43a5062a..45a34f555e7 100644
--- a/ui/tests/integration/testGoAgentDataCollection.php
+++ b/ui/tests/integration/testGoAgentDataCollection.php
@@ -381,12 +381,18 @@ class testGoAgentDataCollection extends CIntegrationTest {
'value_type' => $item['valueType'],
'delay' => '1s'
];
-
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
- $items[] = array_merge($data, [
- 'hostid' => self::$hostids[$component],
- 'interfaceid' => $interfaceids[$component]
- ]);
+ $host_if_props = [
+ 'hostid' => self::$hostids[$component]
+ ];
+
+ if ($data['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ $host_if_props['interfaceid'] = 0;
+ } else {
+ $host_if_props['interfaceid'] = $interfaceids[$component];
+ }
+
+ $items[] = array_merge($data, $host_if_props);
}
}
diff --git a/ui/tests/integration/testInitialConfSync.php b/ui/tests/integration/testInitialConfSync.php
index a1e7d7f222b..1f28d058f59 100644
--- a/ui/tests/integration/testInitialConfSync.php
+++ b/ui/tests/integration/testInitialConfSync.php
@@ -25,7 +25,7 @@ require_once dirname(__FILE__) . '/../include/CIntegrationTest.php';
*
* @required-components server
* @configurationDataProvider serverConfigurationProvider
- * @backup actions, config, functions, globalmacro
+ * @backup actions, config, config_autoreg_tls, functions, globalmacro
* @backup group_prototype, host_discovery, host_inventory, hostmacro, host_rtdata, hosts, hosts_groups, hosts_templates
* @backup host_tag, hstgrp, interface, item_condition, item_discovery, item_parameter, item_preproc, item_rtdata, items
* @backup item_tag, lld_macro_path, lld_override, lld_override_condition, lld_override_opdiscover, lld_override_operation
@@ -396,7 +396,7 @@ class testInitialConfSync extends CIntegrationTest
"insert" =>
"0",
"update" =>
- "72",
+ "70",
"delete" =>
"0"
]
@@ -594,7 +594,7 @@ class testInitialConfSync extends CIntegrationTest
"insert" =>
"0",
"update" =>
- "10",
+ "12",
"delete" =>
"0"
]
diff --git a/ui/tests/integration/testItemState.php b/ui/tests/integration/testItemState.php
index 25c70bf4d49..3d8c610bc5a 100644
--- a/ui/tests/integration/testItemState.php
+++ b/ui/tests/integration/testItemState.php
@@ -100,7 +100,7 @@ class testItemState extends CIntegrationTest {
// Create items
foreach (self::$items as $key => $item) {
- $items[] = [
+ $new_item = [
'name' => $key,
'key_' => $item['key'],
'type' => $item['type'],
@@ -110,6 +110,14 @@ class testItemState extends CIntegrationTest {
'delay' => '1s',
'status' => ITEM_STATUS_DISABLED
];
+
+ if ($new_item['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ $new_item['interfaceid'] = 0;
+ } else {
+ $new_item['interfaceid'] = self::$interfaceid;
+ }
+
+ $items[] = $new_item;
}
$response = $this->call('item.create', $items);
diff --git a/ui/tests/selenium/common/testCalculatedFormula.php b/ui/tests/selenium/common/testCalculatedFormula.php
index e7fec0ba368..b8843a89f3c 100644
--- a/ui/tests/selenium/common/testCalculatedFormula.php
+++ b/ui/tests/selenium/common/testCalculatedFormula.php
@@ -3453,7 +3453,7 @@ class testCalculatedFormula extends CWebTest {
$this->page->login()->open($this->url)->waitUntilReady();
$form = $this->query('name:itemForm')->asForm()->waitUntilVisible()->one();
- $key = 'calc'.microtime(true);
+ $key = 'calc'.microtime(true).'[{#KEY}]';
$form->fill([
'Name' => 'Calc',
diff --git a/ui/tests/selenium/common/testFormPreprocessing.php b/ui/tests/selenium/common/testFormPreprocessing.php
index 0b1c5449f1a..91794f6cf8a 100644
--- a/ui/tests/selenium/common/testFormPreprocessing.php
+++ b/ui/tests/selenium/common/testFormPreprocessing.php
@@ -191,12 +191,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Right trim',
- 'Key' => 'empty-right-trim'
+ 'Key' => 'empty-right-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Right trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -204,12 +204,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Left trim',
- 'Key' => 'empty-left-trim'
+ 'Key' => 'empty-left-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Left trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -217,12 +217,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Trim',
- 'Key' => 'empty-trim'
+ 'Key' => 'empty-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Arithmetic. Custom multiplier.
@@ -231,12 +231,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty multiplier',
- 'Key' => 'empty-multiplier'
+ 'Key' => 'empty-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -244,12 +244,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'String multiplier',
- 'Key' => 'string-multiplier'
+ 'Key' => 'string-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => 'abc']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -257,12 +257,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Multiplier comma',
- 'Key' => 'comma-multiplier'
+ 'Key' => 'comma-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => '0,0']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -270,12 +270,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Multiplier symbols',
- 'Key' => 'symbols-multiplier'
+ 'Key' => 'symbols-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => '1a!@#$%^&*()-=']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
// Change. Simple change, Change per second
@@ -284,13 +284,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two delta',
- 'Key' => 'two-delta'
+ 'Key' => 'two-delta[{#KEY}]'
],
'preprocessing' => [
['type' => 'Simple change'],
['type' => 'Simple change']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -298,13 +299,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two delta per second',
- 'Key' => 'two-delta-per-second'
+ 'Key' => 'two-delta-per-second[{#KEY}]'
],
'preprocessing' => [
['type' => 'Change per second'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -312,13 +314,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two different delta',
- 'Key' => 'two-different-delta'
+ 'Key' => 'two-different-delta[{#KEY}]'
],
'preprocessing' => [
['type' => 'Simple change'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
// Validation. In range.
@@ -327,12 +330,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range empty',
- 'Key' => 'in-range-empty'
+ 'Key' => 'in-range-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params": cannot be empty.'
]
],
[
@@ -340,12 +343,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range letters string',
- 'Key' => 'in-range-letters-string'
+ 'Key' => 'in-range-letters-string[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => 'abc', 'parameter_2' => 'def']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -353,12 +356,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range symbols',
- 'Key' => 'in-range-symbols'
+ 'Key' => 'in-range-symbols[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '1a!@#$%^&*()-=', 'parameter_2' => '2b!@#$%^&*()-=']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -366,12 +369,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range comma',
- 'Key' => 'in-range-comma'
+ 'Key' => 'in-range-comma[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '1,5', 'parameter_2' => '-3,5']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -379,12 +382,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range wrong interval',
- 'Key' => 'in-range-wrong-interval'
+ 'Key' => 'in-range-wrong-interval[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '8', 'parameter_2' => '-8']
],
- 'error' => 'Incorrect value for field "params": "min" value must be less than or equal to "max" value.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be less than or equal '.
+ 'to the value of parameter "/1/preprocessing/1/params/1".'
]
],
// Validation. Matches regular expression.
@@ -393,12 +397,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Matches regular expression empty',
- 'Key' => 'matches-regular-expression-empty'
+ 'Key' => 'matches-regular-expression-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Matches regular expression']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Validation. Check for error in XML.
@@ -407,12 +411,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error XML empty',
- 'Key' => 'item-error-xml-empty'
+ 'Key' => 'item-error-xml-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error in XML']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Validation. Check for error using regular expression.
@@ -421,12 +425,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP both params empty',
- 'Key' => 'item-error-regexp-both-empty'
+ 'Key' => 'item-error-regexp-both-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -434,12 +438,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP first parameter empty',
- 'Key' => 'item-error-regexp-first-empty'
+ 'Key' => 'item-error-regexp-first-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression', 'parameter_2' => 'test']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -447,12 +451,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP second parameter empty',
- 'Key' => 'item-error-regexp-second-empty'
+ 'Key' => 'item-error-regexp-second-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression', 'parameter_1' => 'test']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.'
]
],
// Throttling. Discard unchanged.
@@ -461,13 +465,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item two discard unchanged',
- 'Key' => 'item-two-discard-unchanged'
+ 'Key' => 'item-two-discard-unchanged[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((19, 20)).'
]
],
[
@@ -475,13 +480,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item two different throttlings',
- 'Key' => 'item-two-different-throttlings'
+ 'Key' => 'item-two-different-throttlings[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((19, 20)).'
]
]
]);
@@ -498,12 +504,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty regular expression',
- 'Key' => 'Empty-both-parameters'
+ 'Key' => 'Empty-both-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -511,12 +518,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty pattern of regular expression',
- 'Key' => 'empty-first-parameter'
+ 'Key' => 'empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression', 'parameter_2' => 'test output']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -524,12 +532,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty output of regular expression',
- 'Key' => 'empty-second-parameter'
+ 'Key' => 'empty-second-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => 'expression']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": second parameter is expected.'
]
],
// Structured data. XML XPath.
@@ -538,12 +547,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'XML XPath',
- 'Key' => 'empty-xpath'
+ 'Key' => 'empty-xpath[{#KEY}]'
],
'preprocessing' => [
['type' => 'XML XPath']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Structured data. JSONPath.
@@ -552,12 +562,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'JSONPath empty',
- 'Key' => 'empty-jsonpath'
+ 'Key' => 'empty-jsonpath[{#KEY}]'
],
'preprocessing' => [
['type' => 'JSONPath']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Custom scripts. JavaScript.
@@ -566,12 +577,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty JavaScript',
- 'Key' => 'item-empty-javascript'
+ 'Key' => 'item-empty-javascript[{#KEY}]'
],
'preprocessing' => [
['type' => 'JavaScript']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Validation. Does not match regular expression
@@ -580,12 +592,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Does not match regular expression empty',
- 'Key' => 'does-not-match-regular-expression-empty'
+ 'Key' => 'does-not-match-regular-expression-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Does not match regular expression']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Validation. Check for error in JSON.
@@ -594,12 +607,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Error JSON empty',
- 'Key' => 'error-json-empty'
+ 'Key' => 'error-json-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error in JSON']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Throttling. Discard unchanged with heartbeat
@@ -608,13 +622,15 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two equal discard unchanged with heartbeat',
- 'Key' => 'two-equal-discard-unchanged-with-heartbeat'
+ 'Key' => 'two-equal-discard-unchanged-with-heartbeat[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist '.
+ 'within the combinations of (type)=((19, 20)).',
+ 'lld_error' => 'Only one throttling step is allowed'
]
],
[
@@ -622,13 +638,15 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two different discard unchanged with heartbeat',
- 'Key' => 'two-different-discard-unchanged-with-heartbeat'
+ 'Key' => 'two-different-discard-unchanged-with-heartbeat[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '2']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist '.
+ 'within the combinations of (type)=((19, 20)).',
+ 'lld_error' => 'Only one throttling step is allowed'
]
],
[
@@ -636,12 +654,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat empty',
- 'Key' => 'discard-unchanged-with-heartbeat-empty'
+ 'Key' => 'discard-unchanged-with-heartbeat-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat']
],
- 'error' => 'Invalid parameter "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Invalid parameter "params": cannot be empty.'
]
],
[
@@ -649,12 +668,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat symbols',
- 'Key' => 'discard-unchanged-with-heartbeat-symbols'
+ 'Key' => 'discard-unchanged-with-heartbeat-symbols[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '3g!@#$%^&*()-=']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -662,12 +682,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discardunchanged with heartbeat letters string',
- 'Key' => 'discard-unchanged-with-heartbeat-letters-string'
+ 'Key' => 'discard-unchanged-with-heartbeat-letters-string[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => 'abc']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -675,12 +696,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat comma',
- 'Key' => 'discard-unchanged-with-heartbeat-comma'
+ 'Key' => 'discard-unchanged-with-heartbeat-comma[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1,5']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -688,12 +710,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat dot',
- 'Key' => 'discard-unchanged-with-heartbeat-dot'
+ 'Key' => 'discard-unchanged-with-heartbeat-dot[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1.5']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -701,12 +724,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat negative',
- 'Key' => 'discard-unchanged-with-heartbeat-negative'
+ 'Key' => 'discard-unchanged-with-heartbeat-negative[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '-3']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
],
[
@@ -714,12 +738,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat zero',
- 'Key' => 'discard-unchanged-with-heartbeat-zero'
+ 'Key' => 'discard-unchanged-with-heartbeat-zero[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '0']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
],
[
@@ -727,12 +752,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat maximum',
- 'Key' => 'unchanged-with-heartbeat-max'
+ 'Key' => 'unchanged-with-heartbeat-max[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '788400001']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
]
];
@@ -749,7 +775,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON empty parameters',
- 'Key' => 'csv-to-json-empty-parameters'
+ 'Key' => 'csv-to-json-empty-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON']
@@ -761,7 +787,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON with default parameters',
- 'Key' => 'csv-to-json-with-default-parameters'
+ 'Key' => 'csv-to-json-with-default-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON', 'parameter_1' => ',', 'parameter_2' => '"', 'parameter_3' => true]
@@ -773,7 +799,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON custom parameters',
- 'Key' => 'csv-to-json-custom-parameters'
+ 'Key' => 'csv-to-json-custom-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON', 'parameter_1' => ' ', 'parameter_2' => "'", 'parameter_3' => false]
@@ -786,7 +812,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'In range negative float',
- 'Key' => 'in-range-negative-float'
+ 'Key' => 'in-range-negative-float[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '-3.5', 'parameter_2' => '-1.5']
@@ -799,7 +825,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Not supported step',
- 'Key' => 'check-for-not-supported'
+ 'Key' => 'check-for-not-supported[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for not supported value']
@@ -811,10 +837,10 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'In range zero',
- 'Key' => 'in-range-zero'
+ 'Key' => 'in-range-zero[{#KEY}]'
],
'preprocessing' => [
- ['type' => 'In range', 'parameter_1' => '0', 'parameter_2' => '0']
+ ['type' => 'In range', 'parameter_1' => '0', 'parameter_2' => '1']
]
]
],
@@ -823,7 +849,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add all preprocessing',
- 'Key' => 'item.all.preprocessing'
+ 'Key' => 'item.all.preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for not supported value'],
@@ -852,7 +878,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add symbols preprocessing',
- 'Key' => 'item.symbols.preprocessing'
+ 'Key' => 'item.symbols.preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => '1a!@#$%^&*()-=', 'parameter_2' => '1a!@#$%^&*()-='],
@@ -861,7 +887,7 @@ abstract class testFormPreprocessing extends CWebTest {
['type' => 'Trim', 'parameter_1' => '3c!@#$%^&*()-='],
['type' => 'XML XPath', 'parameter_1' => '3c!@#$%^&*()-='],
['type' => 'JSONPath', 'parameter_1' => '3c!@#$%^&*()-='],
- ['type' => 'Custom multiplier', 'parameter_1' => '4e+10'],
+ ['type' => 'Custom multiplier', 'parameter_1' => '4.0E+14'],
['type' => 'Regular expression', 'parameter_1' => '5d!@#$%^&*()-=', 'parameter_2' => '6e!@#$%^&*()-='],
['type' => 'JavaScript', 'parameter_1' => '5d!@#$%^&*()-='],
['type' => 'Matches regular expression', 'parameter_1' => '7f!@#$%^&*()-='],
@@ -877,7 +903,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add the same preprocessing',
- 'Key' => 'item.theSamePpreprocessing'
+ 'Key' => 'item.theSamePpreprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => 'текст', 'parameter_2' => 'замена'],
@@ -929,7 +955,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item with preprocessing rule with user macro',
- 'Key' => 'item-user-macro'
+ 'Key' => 'item-user-macro[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => '{$TEST1}', 'parameter_2' => '{$MACRO2}'],
@@ -957,7 +983,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item with spaces in preprocessing',
- 'Key' => 'item-spaces-preprocessing'
+ 'Key' => 'item-spaces-preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => ' test text ', 'parameter_2' => ' replacement 1 '],
@@ -990,12 +1016,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter starts with digits',
- 'Key' => 'json-prometeus-digits-first-parameter'
+ 'Key' => 'json-prometeus-digits-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '1name_of_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1003,12 +1030,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong equals operator',
- 'Key' => 'json-prometeus-wrong-equals-operator'
+ 'Key' => 'json-prometeus-wrong-equals-operator[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1016,12 +1044,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator >',
- 'Key' => 'json-prometeus-unsupported-operator-1'
+ 'Key' => 'json-prometeus-unsupported-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}>1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1029,12 +1058,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator <',
- 'Key' => 'json-prometeus-unsupported-operator-2'
+ 'Key' => 'json-prometeus-unsupported-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1042,12 +1072,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator !==',
- 'Key' => 'json-prometeus-unsupported-operator-3'
+ 'Key' => 'json-prometeus-unsupported-operator-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}!==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1055,12 +1086,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator >=',
- 'Key' => 'json-prometeus-unsupported-operator-4'
+ 'Key' => 'json-prometeus-unsupported-operator-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}>=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1068,12 +1100,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator =<',
- 'Key' => 'json-prometeus-unsupported-operator-5'
+ 'Key' => 'json-prometeus-unsupported-operator-5[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}=<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1081,12 +1114,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON duplicate metric condition',
- 'Key' => 'json-duplicate-metric-condition'
+ 'Key' => 'json-duplicate-metric-condition[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_system{__name__="metric_name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1094,12 +1128,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong parameter - space',
- 'Key' => 'json-wrong-parameter-space'
+ 'Key' => 'json-wrong-parameter-space[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu usage_system']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1107,13 +1142,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong parameter - slash',
- 'Key' => 'json-wrong-parameter-slash'
+ 'Key' => 'json-wrong-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu\\']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1121,13 +1157,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong parameter - digits',
- 'Key' => 'json-wrong-parameter-digits'
+ 'Key' => 'json-wrong-parameter-digits[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '123']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1135,13 +1172,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong first parameter - pipe',
- 'Key' => 'json-wrong-parameter-pipe'
+ 'Key' => 'json-wrong-parameter-pipe[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'metric==1e|5']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1149,13 +1187,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong first parameter - slash',
- 'Key' => 'json-wrong-parameter-slash'
+ 'Key' => 'json-wrong-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label="value\"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1163,27 +1202,28 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item duplicate Prometeus to JSON steps',
- 'Key' => 'duplicate-prometheus-to-json-steps'
+ 'Key' => 'duplicate-prometheus-to-json-steps[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system_1'],
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system_1']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((22, 23)).',
+ 'lld_error' => 'Only one Prometheus step is allowed.'
]
],
- // Successful Prometheus to JSON creation.
[
[
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON empty first parameter',
- 'Key' => 'json-prometeus-empty-first-parameter'
+ 'Key' => 'json-prometeus-empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1191,7 +1231,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter +inf',
- 'Key' => 'json-prometeus-plus-inf'
+ 'Key' => 'json-prometeus-plus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system==+inf']
@@ -1203,7 +1243,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter inf',
- 'Key' => 'json-prometeus-inf'
+ 'Key' => 'json-prometeus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==inf']
@@ -1215,7 +1255,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter -inf',
- 'Key' => 'json-prometeus-negative-inf'
+ 'Key' => 'json-prometeus-negative-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}==-inf']
@@ -1227,7 +1267,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter nan',
- 'Key' => 'json-prometeus-nan'
+ 'Key' => 'json-prometeus-nan[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==nan']
@@ -1239,7 +1279,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter exp',
- 'Key' => 'json-prometeus-exp'
+ 'Key' => 'json-prometeus-exp[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system==3.5180e+11']
@@ -1251,7 +1291,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter ==1',
- 'Key' => 'json-prometeus-neutral-digit'
+ 'Key' => 'json-prometeus-neutral-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==1']
@@ -1263,7 +1303,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameters ==+1',
- 'Key' => 'json-prometeus-positive-digit'
+ 'Key' => 'json-prometeus-positive-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==+1']
@@ -1275,7 +1315,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameters ==-1',
- 'Key' => 'json-prometeus-negative-digit'
+ 'Key' => 'json-prometeus-negative-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==-1']
@@ -1287,7 +1327,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON label operator =',
- 'Key' => 'json-prometeus-label-operator-equal-strong'
+ 'Key' => 'json-prometeus-label-operator-equal-strong[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="name"}']
@@ -1299,7 +1339,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON label operator =~',
- 'Key' => 'json-prometeus-label-operator-contains'
+ 'Key' => 'json-prometeus-label-operator-contains[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name=~"name"}']
@@ -1311,7 +1351,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON slashes in pattern',
- 'Key' => 'json-prometeus-slashes-pattern'
+ 'Key' => 'json-prometeus-slashes-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label="value\\\\"}']
@@ -1323,7 +1363,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameter',
- 'Key' => 'json-prometeus-macros-1'
+ 'Key' => 'json-prometeus-macros-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{$METRIC_NAME}==1']
@@ -1335,7 +1375,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameter',
- 'Key' => 'json-prometeus-macros-2'
+ 'Key' => 'json-prometeus-macros-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="{$METRIC_NAME}"}']
@@ -1347,7 +1387,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameters',
- 'Key' => 'json-prometeus-macros-3'
+ 'Key' => 'json-prometeus-macros-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{{$LABEL_NAME}="<label value>"}']
@@ -1359,7 +1399,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameters',
- 'Key' => 'json-prometeus-macros-4'
+ 'Key' => 'json-prometeus-macros-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="{$LABEL_VALUE}"}']
@@ -1371,7 +1411,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported label operator !=',
- 'Key' => 'json-prometeus-unsupported-label-operator-1'
+ 'Key' => 'json-prometeus-unsupported-label-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name!="regex_pattern"}']
@@ -1383,7 +1423,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported label operator !~',
- 'Key' => 'json-prometeus-unsupported-label-operator-2'
+ 'Key' => 'json-prometeus-unsupported-label-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name!~"<regex>"}']
@@ -1404,12 +1444,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus empty first parameter',
- 'Key' => 'prometeus-empty-first-parameter'
+ 'Key' => 'prometeus-empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -1417,12 +1457,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Prometheus space in pattern',
- 'Key' => 'prometheus-space-in-pattern'
+ 'Key' => 'prometheus-space-in-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu usage_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1430,12 +1470,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Prometheus only digits in pattern',
- 'Key' => 'prometheus-digits-in-pattern'
+ 'Key' => 'prometheus-digits-in-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '1223']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1443,12 +1483,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus first parameter starts with digits',
- 'Key' => 'prometeus-digits-first-parameter'
+ 'Key' => 'prometeus-digits-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '1name_of_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1456,12 +1496,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong equals operator',
- 'Key' => 'rometeus-wrong-equals-operator'
+ 'Key' => 'rometeus-wrong-equals-operator[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1469,12 +1509,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator >',
- 'Key' => 'prometeus-unsupported-operator-1'
+ 'Key' => 'prometeus-unsupported-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}>1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1482,12 +1522,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator <',
- 'Key' => 'prometeus-unsupported-operator-2'
+ 'Key' => 'prometeus-unsupported-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1495,12 +1535,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator !==',
- 'Key' => 'prometeus-unsupported-operator-3'
+ 'Key' => 'prometeus-unsupported-operator-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}!==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1508,12 +1548,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator >=',
- 'Key' => 'prometeus-unsupported-operator-4'
+ 'Key' => 'prometeus-unsupported-operator-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}>=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1521,12 +1561,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator =<',
- 'Key' => 'prometeus-unsupported-operator-5'
+ 'Key' => 'prometeus-unsupported-operator-5[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}=<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1534,12 +1574,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus duplicate metric condition',
- 'Key' => 'duplicate-metric-condition'
+ 'Key' => 'duplicate-metric-condition[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_system{__name__="metric_name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1547,13 +1587,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item duplicate Prometeus steps',
- 'Key' => 'duplicate-prometheus-steps'
+ 'Key' => 'duplicate-prometheus-steps[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system'],
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((22, 23)).'
]
],
[
@@ -1561,7 +1602,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - space',
- 'Key' => 'wrong-second-parameter-space'
+ 'Key' => 'wrong-second-parameter-space[{#KEY}]'
],
'preprocessing' => [
[
@@ -1571,7 +1612,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => 'label name'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1579,7 +1620,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - quotes',
- 'Key' => 'wrong-second-parameter-quotes'
+ 'Key' => 'wrong-second-parameter-quotes[{#KEY}]'
],
'preprocessing' => [
[
@@ -1589,7 +1630,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '"label_name"'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1597,7 +1638,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - triangle quotes',
- 'Key' => 'wrong-second-parameter-triangle-quotes'
+ 'Key' => 'wrong-second-parameter-triangle-quotes[{#KEY}]'
],
'preprocessing' => [
[
@@ -1607,7 +1648,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '<label_name>'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1615,7 +1656,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - slash',
- 'Key' => 'wrong-second-parameter-slash'
+ 'Key' => 'wrong-second-parameter-slash[{#KEY}]'
],
'preprocessing' => [
[
@@ -1625,7 +1666,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '\0'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1633,7 +1674,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - digits',
- 'Key' => 'wrong-second-parameter-digits'
+ 'Key' => 'wrong-second-parameter-digits[{#KEY}]'
],
'preprocessing' => [
[
@@ -1643,7 +1684,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '123'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1651,13 +1692,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter - pipe',
- 'Key' => 'wrong-second-parameter-pipe'
+ 'Key' => 'wrong-second-parameter-pipe[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'metric==1e|5']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1665,13 +1706,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter - slash',
- 'Key' => 'wrong-second-parameter-slash'
+ 'Key' => 'wrong-second-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label="value\"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1679,13 +1720,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter =!',
- 'Key' => 'wrong-second-parameter-equals-exlamation'
+ 'Key' => 'wrong-second-parameter-equals-exlamation[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name=!"name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1693,13 +1734,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter ~!',
- 'Key' => 'wrong-second-parameter-tilda-exclamation'
+ 'Key' => 'wrong-second-parameter-tilda-exclamation[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name~!"name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
// Successful Prometheus pattern creation.
@@ -1708,7 +1749,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus empty second parameter',
- 'Key' => 'prometeus-empty-second-parameter'
+ 'Key' => 'prometeus-empty-second-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system']
@@ -1720,7 +1761,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus both parameters present',
- 'Key' => 'prometeus-both-parameters-present'
+ 'Key' => 'prometeus-both-parameters-present[{#KEY}]'
],
'preprocessing' => [
[
@@ -1737,7 +1778,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter +inf',
- 'Key' => 'prometeus-plus-inf'
+ 'Key' => 'prometeus-plus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system==+inf']
@@ -1749,7 +1790,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter inf',
- 'Key' => 'prometeus-inf'
+ 'Key' => 'prometeus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==inf']
@@ -1761,7 +1802,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter -inf',
- 'Key' => 'prometeus-negative-inf'
+ 'Key' => 'prometeus-negative-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}==-inf']
@@ -1773,7 +1814,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter nan',
- 'Key' => 'prometeus-nan'
+ 'Key' => 'prometeus-nan[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==nan']
@@ -1785,7 +1826,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter exp',
- 'Key' => 'prometeus-exp'
+ 'Key' => 'prometeus-exp[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system==3.5180e+11']
@@ -1797,7 +1838,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter ==1',
- 'Key' => 'prometeus-neutral-digit'
+ 'Key' => 'prometeus-neutral-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==1']
@@ -1809,7 +1850,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameters ==+1',
- 'Key' => 'prometeus-positive-digit'
+ 'Key' => 'prometeus-positive-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==+1']
@@ -1821,7 +1862,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameters ==-1',
- 'Key' => 'prometeus-negative-digit'
+ 'Key' => 'prometeus-negative-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==-1']
@@ -1833,7 +1874,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator =',
- 'Key' => 'prometeus-label-operator-equal-strong'
+ 'Key' => 'prometeus-label-operator-equal-strong[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name="name"}']
@@ -1845,7 +1886,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator =~',
- 'Key' => 'prometeus-label-operator-contains'
+ 'Key' => 'prometeus-label-operator-contains[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name=~"name"}']
@@ -1857,7 +1898,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator !=',
- 'Key' => 'prometeus-label-operator-exclamation-equals'
+ 'Key' => 'prometeus-label-operator-exclamation-equals[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!="name"}']
@@ -1869,7 +1910,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator !~',
- 'Key' => 'prometeus-label-operator-exclamation-tilda'
+ 'Key' => 'prometeus-label-operator-exclamation-tilda[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!~"name"}']
@@ -1881,7 +1922,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus slashes in pattern',
- 'Key' => 'prometeus-slashes-pattern'
+ 'Key' => 'prometeus-slashes-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label="value\\\\"}']
@@ -1893,7 +1934,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-1'
+ 'Key' => 'prometeus-macros-1[{#KEY}]'
],
'preprocessing' => [
[
@@ -1909,7 +1950,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-2'
+ 'Key' => 'prometeus-macros-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="{$METRIC_NAME}"}']
@@ -1921,7 +1962,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-3'
+ 'Key' => 'prometeus-macros-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{{$LABEL_NAME}="<label value>"}']
@@ -1933,7 +1974,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-4'
+ 'Key' => 'prometeus-macros-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name="{$LABEL_VALUE}"}']
@@ -1945,7 +1986,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus unsupported label operator !=',
- 'Key' => 'prometeus-unsupported-label-operator-1'
+ 'Key' => 'prometeus-unsupported-label-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!="regex_pattern"}']
@@ -1957,7 +1998,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus unsupported label operator !~',
- 'Key' => 'prometeus-unsupported-label-operator-2'
+ 'Key' => 'prometeus-unsupported-label-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!~"<regex>"}']
@@ -1969,7 +2010,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function sum',
- 'Key' => 'prometeus-function-sum'
+ 'Key' => 'prometeus-function-sum[{#KEY}]'
],
'preprocessing' => [
[
@@ -1985,7 +2026,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function min',
- 'Key' => 'prometeus-function-min'
+ 'Key' => 'prometeus-function-min[{#KEY}]'
],
'preprocessing' => [
[
@@ -2001,7 +2042,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function max',
- 'Key' => 'prometeus-function-max'
+ 'Key' => 'prometeus-function-max[{#KEY}]'
],
'preprocessing' => [
[
@@ -2017,7 +2058,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function avg',
- 'Key' => 'prometeus-function-avg'
+ 'Key' => 'prometeus-function-avg[{#KEY}]'
],
'preprocessing' => [
[
@@ -2033,7 +2074,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function count',
- 'Key' => 'prometeus-function-count'
+ 'Key' => 'prometeus-function-count[{#KEY}]'
],
'preprocessing' => [
[
@@ -2090,23 +2131,21 @@ abstract class testFormPreprocessing extends CWebTest {
$form->submit();
$this->page->waitUntilReady();
- switch ($data['expected']) {
- case TEST_GOOD:
- $this->assertMessage(TEST_GOOD, $this->success_message);
+ if ($data['expected'] === TEST_GOOD) {
+ $this->assertMessage(TEST_GOOD, $this->success_message);
- // Check result in frontend form.
- $id = CDBHelper::getValue('SELECT itemid FROM items WHERE key_='.zbx_dbstr($data['fields']['Key']));
- $this->page->open($this->ready_link.$id);
- $form->selectTab('Preprocessing');
- $this->assertPreprocessingSteps($data['preprocessing']);
- break;
-
- case TEST_BAD:
- $this->assertMessage(TEST_BAD, $this->fail_message, $data['error']);
+ // Check result in frontend form.
+ $id = CDBHelper::getValue('SELECT itemid FROM items WHERE key_='.zbx_dbstr($data['fields']['Key']));
+ $this->page->open($this->ready_link.$id);
+ $form->selectTab('Preprocessing');
+ $this->assertPreprocessingSteps($data['preprocessing']);
+ }
+ else {
+ $error_message = ($lld) ? $data['lld_error'] : $data['error'];
+ $this->assertMessage(TEST_BAD, $this->fail_message, $error_message);
- // Check that DB hash is not changed.
- $this->assertEquals($old_hash, CDBHelper::getHash($sql_items));
- break;
+ // Check that DB hash is not changed.
+ $this->assertEquals($old_hash, CDBHelper::getHash($sql_items));
}
}
@@ -2119,7 +2158,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Prometheus to JSON trailing spaces',
- 'Key' => 'json-prometeus-space-in-parameter'
+ 'Key' => 'json-prometeus-space-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => ' metric ']
@@ -2138,7 +2177,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Prometheus pattern trailing spaces',
- 'Key' => 'prometeus-space-in-parameters'
+ 'Key' => 'prometeus-space-in-parameters[{#KEY}]'
],
'preprocessing' => [
[
@@ -2154,7 +2193,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Custom multiplier trailing spaces',
- 'Key' => 'Custom-multiplier-spaces-in-parameter'
+ 'Key' => 'Custom-multiplier-spaces-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => ' 2 ']
@@ -2165,7 +2204,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'In range trailing spaces',
- 'Key' => 'in-range-spaces-in-parameter'
+ 'Key' => 'in-range-spaces-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => ' 5 ', 'parameter_2' => ' 10 ']
@@ -2242,7 +2281,7 @@ abstract class testFormPreprocessing extends CWebTest {
$case = [
'fields' => [
'Name' => 'Preprocessing '.$label,
- 'Key' => 'preprocessing-steps-discard-on-fail'.$value
+ 'Key' => 'preprocessing-steps-discard-on-fail'.$value.'[{#KEY}]'
],
'preprocessing' => [
[
@@ -2461,7 +2500,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value empty',
- 'Key' => 'set-value-empty'
+ 'Key' => 'set-value-empty[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2472,7 +2511,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value number',
- 'Key' => 'set-value-number'
+ 'Key' => 'set-value-number[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2483,7 +2522,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value string',
- 'Key' => 'set-value-string'
+ 'Key' => 'set-value-string[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2494,7 +2533,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value special-symbols',
- 'Key' => 'set-value-special-symbols'
+ 'Key' => 'set-value-special-symbols[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2506,19 +2545,20 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Set error empty',
- 'Key' => 'set-error-empty'
+ 'Key' => 'set-error-empty[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
'error_handler_params' => ''
],
- 'error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/error_handler_params": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
],
[
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error string',
- 'Key' => 'set-error-string'
+ 'Key' => 'set-error-string[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2529,7 +2569,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error number',
- 'Key' => 'set-error-number'
+ 'Key' => 'set-error-number[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2540,7 +2580,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error special symbols',
- 'Key' => 'set-error-special-symbols'
+ 'Key' => 'set-error-special-symbols[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2590,7 +2630,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Templated Preprocessing steps',
- 'Key' => 'templated-preprocessing-steps'
+ 'Key' => 'templated-preprocessing-steps[{#KEY}]'
],
'preprocessing' => [
[
@@ -2776,7 +2816,7 @@ abstract class testFormPreprocessing extends CWebTest {
protected function checkCloneItem($link, $item, $templated = false) {
$cloned_values = [
'Name' => 'Cloned_testInheritancePreprocessingSteps'.time(),
- 'Key' => 'cloned-preprocessing'.time()
+ 'Key' => 'cloned-preprocessing'.time().'[{#KEY}]'
];
// Open original item on host and get its' preprocessing steps.
diff --git a/ui/tests/selenium/common/testFormTags.php b/ui/tests/selenium/common/testFormTags.php
index fa306fb1ea0..e951c7efaab 100644
--- a/ui/tests/selenium/common/testFormTags.php
+++ b/ui/tests/selenium/common/testFormTags.php
@@ -257,7 +257,7 @@ class testFormTags extends CWebTest {
case 'item prototype':
$sql = 'SELECT * FROM items ORDER BY itemid';
$locator = 'name:itemForm';
- $fields = ['Name' => $data['name'], 'Key' => 'itemtag_'.microtime(true), 'Type' => 'Zabbix trapper'];
+ $fields = ['Name' => $data['name'], 'Key' => 'itemtag_'.microtime(true).'[{#KEY}]', 'Type' => 'Zabbix trapper'];
break;
case 'web scenario':
@@ -621,7 +621,7 @@ class testFormTags extends CWebTest {
case 'item':
case 'item prototype':
$form = $this->query('name:itemForm')->asForm()->waitUntilPresent()->one();
- $form->fill(['Name' => $new_name, 'Key' => 'newkey_'.microtime(true)]);
+ $form->fill(['Name' => $new_name, 'Key' => 'newkey_'.microtime(true).'[{#KEY}]']);
$sql_old_name = 'SELECT NULL FROM items WHERE name='.zbx_dbstr($this->clone_name);
$sql_new_name = 'SELECT NULL FROM items WHERE name='.zbx_dbstr($new_name);
break;
diff --git a/ui/tests/selenium/common/testItemTest.php b/ui/tests/selenium/common/testItemTest.php
index eecaad2af7e..080b89e54db 100644
--- a/ui/tests/selenium/common/testItemTest.php
+++ b/ui/tests/selenium/common/testItemTest.php
@@ -58,10 +58,10 @@ class testItemTest extends CWebTest {
['Type' => 'Database monitor', 'SQL query' => 'query'],
['Type' => 'HTTP agent', 'URL' => 'https://www.zabbix.com'],
['Type' => 'IPMI agent', 'IPMI sensor' => 'Sensor'],
- ['Type' => 'SSH agent', 'Key' => 'ssh.run[Description,127.0.0.1,50]', 'User name' => 'Name', 'Executed script' => 'Script'],
- ['Type' => 'TELNET agent', 'Key' => 'telnet'],
- ['Type' => 'JMX agent', 'Key' => 'jmx','JMX endpoint' => 'service:jmx:rmi:///jndi/rmi://{HOST.CONN}:{HOST.PORT}/jmxrmi', 'User name' => ''],
- ['Type' => 'Dependent item', 'Key'=>'dependent', 'Master item' => 'Master item']
+ ['Type' => 'SSH agent', 'Key' => 'ssh.run[Description,127.0.0.1,50,[{#KEY}]]', 'User name' => 'Name', 'Executed script' => 'Script'],
+ ['Type' => 'TELNET agent', 'Key' => 'telnet[{#KEY}]'],
+ ['Type' => 'JMX agent', 'Key' => 'jmx[{#KEY}]','JMX endpoint' => 'service:jmx:rmi:///jndi/rmi://{HOST.CONN}:{HOST.PORT}/jmxrmi', 'User name' => ''],
+ ['Type' => 'Dependent item', 'Key'=>'dependent[{#KEY}]', 'Master item' => 'Master item']
];
}
@@ -70,7 +70,7 @@ class testItemTest extends CWebTest {
*/
public function getItemTestButtonStateData() {
return array_merge($this->getCommonTestButtonStateData(), [
- ['Type' => 'SNMP trap', 'Key' => 'snmptrap.fallback'],
+ ['Type' => 'SNMP trap', 'Key' => 'snmptrap.fallback[{#KEY}]'],
['Type' => 'Calculated', 'Formula' => '"formula"']
]);
}
@@ -103,7 +103,7 @@ class testItemTest extends CWebTest {
$item_form->fill([
'Name' => $item_name,
'Type' => 'Zabbix agent',
- 'Key' => 'key'
+ 'Key' => 'key[{#KEY}]'
]);
// Check Test item button.
$this->checkTestButtonInPreprocessing($item_type);
@@ -387,7 +387,8 @@ class testItemTest extends CWebTest {
'Type' => 'Zabbix agent',
'Key' => ''
],
- 'error' => 'Incorrect value for field "key_": key is empty.'
+ 'error' => 'Incorrect value for field "key_": key is empty.',
+ 'lld_error' => 'Incorrect value for field "key_": key is empty.'
]
],
[
@@ -397,7 +398,8 @@ class testItemTest extends CWebTest {
'Type' => 'Zabbix agent',
'Key' => 'key space'
],
- 'error' => 'Incorrect value for field "key_": incorrect syntax near " space".'
+ 'error' => 'Incorrect value for field "key_": incorrect syntax near " space".',
+ 'lld_error' => 'Incorrect value for field "key_": incorrect syntax near " space".'
]
],
[
@@ -410,7 +412,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => '', 'parameter_2' => '2']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -423,7 +426,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => '1', 'parameter_2' => '']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/2": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": second parameter is expected.'
]
],
[
@@ -436,7 +440,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'XML XPath', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
[
@@ -454,7 +459,8 @@ class testItemTest extends CWebTest {
'error_handler' => 'Set error to',
'error_handler_params' => '']
],
- 'error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/error_handler_params": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
]
],
[
@@ -740,6 +746,7 @@ class testItemTest extends CWebTest {
foreach ($elements as $name => $selector) {
$elements[$name] = $test_form->query($selector)->one()->detect();
}
+
$proxy = CDBHelper::getValue("SELECT host FROM hosts WHERE hostid IN ".
"(SELECT proxy_hostid FROM hosts WHERE host = 'Test item host')");
@@ -1021,7 +1028,8 @@ class testItemTest extends CWebTest {
}
break;
case TEST_BAD:
- $this->assertMessage(TEST_BAD, null, $data['error']);
+ $error_message = ($lld) ? $data['lld_error'] : $data['error'];
+ $this->assertMessage(TEST_BAD, null, $error_message);
$overlay->close();
break;
}
diff --git a/ui/tests/selenium/common/testMassUpdateItems.php b/ui/tests/selenium/common/testMassUpdateItems.php
index a3b9c6790ef..c41f87b3816 100644
--- a/ui/tests/selenium/common/testMassUpdateItems.php
+++ b/ui/tests/selenium/common/testMassUpdateItems.php
@@ -111,7 +111,7 @@ class testMassUpdateItems extends CWebTest{
'change' => [
'Type' => ['id' => 'type', 'value' => 'SNMP agent']
],
- 'details' => 'Item uses incorrect interface type.'
+ 'details' => 'Invalid parameter "/1/snmp_oid": cannot be empty.'
]
],
[
@@ -124,7 +124,7 @@ class testMassUpdateItems extends CWebTest{
'change' => [
'Type' => ['id' => 'type', 'value' => 'TELNET agent']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/2/username": cannot be empty.'
]
],
[
@@ -138,7 +138,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -153,7 +153,7 @@ class testMassUpdateItems extends CWebTest{
'Authentication method' => ['id' => 'authtype', 'value' => 'Password'],
'User name' => ['id' => 'username', 'value' => '']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -170,7 +170,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => '/path/file1'],
'Private key file' => ['id' => 'privatekey', 'value' => '/path/file2']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -187,7 +187,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => ''],
'Private key file' => ['id' => 'privatekey', 'value' => '/path/file2']
],
- 'details' => 'No public key file specified.'
+ 'details' => 'Invalid parameter "/1/publickey": cannot be empty.'
]
],
[
@@ -204,7 +204,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => '/path/file1'],
'Private key file' => ['id' => 'privatekey', 'value' => '']
],
- 'details' => 'No private key file specified.'
+ 'details' => 'Invalid parameter "/1/privatekey": cannot be empty.'
]
],
[
@@ -222,7 +222,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '3599']
]
],
- 'details' => 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'details' => 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
],
[
@@ -240,7 +240,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '1']
]
],
- 'details' => 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'details' => 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
],
[
@@ -258,7 +258,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '']
]
],
- 'details' => 'Incorrect value for field "history": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/history": cannot be empty.'
]
],
[
@@ -276,7 +276,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '25y']
]
],
- 'details' => 'Incorrect value for field "history": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/history": a time unit is expected.'
]
],
[
@@ -294,7 +294,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '']
]
],
- 'details' => 'Incorrect value for field "trends": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/trends": cannot be empty.'
]
],
[
@@ -312,7 +312,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '86399']
]
],
- 'details' => 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'details' => 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
],
[
@@ -330,7 +330,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '1']
]
],
- 'details' => 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'details' => 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
],
[
@@ -348,7 +348,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '25y']
]
],
- 'details' => 'Incorrect value for field "trends": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/trends": a time unit is expected.'
]
],
[
@@ -362,7 +362,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix trapper'],
'Allowed hosts' => ['id' => 'trapper_hosts', 'value' => 'Zabbix server']
],
- 'details' => 'Incorrect value for field "trapper_hosts": invalid address range "Zabbix server".'
+ 'details' => 'Invalid parameter "/1/trapper_hosts": invalid address range "Zabbix server".'
]
],
[
@@ -376,7 +376,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '']
],
- 'details' => 'Incorrect value for field "delay": invalid delay.'
+ 'details' => 'Invalid parameter "/1/delay": cannot be empty.'
]
],
[
@@ -390,8 +390,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '0']
],
- 'details' => 'Item will not be refreshed. Specified update interval requires having at least '.
- 'one either flexible or scheduling interval.'
+ 'details' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
],
[
@@ -405,8 +404,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '86401']
],
- 'details' => 'Item will not be refreshed. Update interval should be between 1s and 1d. '.
- 'Also Scheduled/Flexible intervals can be used.'
+ 'details' => 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
],
[
@@ -497,7 +495,7 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- 'details' => 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'details' => 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
],
[
@@ -520,7 +518,7 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- 'details' => 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'details' => 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
],
[
@@ -537,7 +535,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'JSON data'],
'Request body' => ['id' => 'posts', 'value' => '"request": "active checks", "host": "host"']
],
- 'details' => 'Invalid parameter "posts": JSON is expected.'
+ 'details' => 'Invalid parameter "/1/posts": JSON is expected.'
]
],
[
@@ -554,7 +552,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'XML data'],
'Request body' => ['id' => 'posts', 'value' => 'xml version="1.0" encoding="UTF-8"?<zabbix_export></zabbix_export>']
],
- 'details' => 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
+ 'details' => 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]
],
[
@@ -571,7 +569,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'XML data'],
'Request body' => ['id' => 'posts', 'value' => '']
],
- 'details' => 'Invalid parameter "posts": XML is expected.'
+ 'details' => 'Invalid parameter "/1/posts": cannot be empty.'
]
],
[
@@ -586,7 +584,7 @@ class testMassUpdateItems extends CWebTest{
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
'URL' => ['id' => 'url', 'value' => '']
],
- 'details' => 'Invalid parameter "/url": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/url": cannot be empty.'
]
],
[
@@ -601,7 +599,7 @@ class testMassUpdateItems extends CWebTest{
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.4:10054'],
'JMX endpoint' => ['id' => 'jmx_endpoint', 'value' => '']
],
- 'details' => 'Incorrect value for field "jmx_endpoint": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/jmx_endpoint": cannot be empty.'
]
],
[
@@ -621,7 +619,7 @@ class testMassUpdateItems extends CWebTest{
'User name' => ['id' => 'username', 'value' => 'new_test_name'],
'Password' => ['id' => 'password', 'value' => '']
],
- 'details' => 'Incorrect value for field "username": both username and password should be either present or empty.'
+ 'details' => 'Invalid parameter "/1": both username and password should be either present or empty.'
]
],
[
@@ -641,7 +639,7 @@ class testMassUpdateItems extends CWebTest{
'User name' => ['id' => 'username', 'value' => ''],
'Password' => ['id' => 'password', 'value' => 'new_test_password']
],
- 'details' => 'Incorrect value for field "username": both username and password should be either present or empty.'
+ 'details' => 'Invalid parameter "/1": both username and password should be either present or empty.'
]
],
[
@@ -655,7 +653,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'SNMP agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.5:10055']
],
- 'details' => 'No SNMP OID specified.',
+ 'details' => 'Invalid parameter "/1/snmp_oid": cannot be empty.',
'interface_text_part' => 'SNMPv3, Context name: zabbix'
]
],
@@ -663,19 +661,6 @@ class testMassUpdateItems extends CWebTest{
[
'expected' => TEST_BAD,
'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'Dependent item']
- ],
- 'details' => 'Incorrect value for field "master_itemid": cannot be empty.'
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'names' => [
'17_Script',
'18_Script'
],
@@ -1126,31 +1111,32 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- [
- [
- 'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
- 'User name' => ['id' => 'username', 'value' => 'telnet_name'],
- 'Password' => ['id' => 'password', 'value' => 'telnet_password']
- ]
- ]
- ],
- [
- [
- 'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'Calculated'],
- 'Type of information' => ['id' => 'value_type', 'value' => 'Numeric (float)']
- ]
- ]
- ],
+ // TODO: uncomment or delete after discussion
+// [
+// [
+// 'names' => [
+// '1_Item',
+// '2_Item'
+// ],
+// 'change' => [
+// 'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
+// 'User name' => ['id' => 'username', 'value' => 'telnet_name'],
+// 'Password' => ['id' => 'password', 'value' => 'telnet_password']
+// ]
+// ]
+// ],
+// [
+// [
+// 'names' => [
+// '1_Item',
+// '2_Item'
+// ],
+// 'change' => [
+// 'Type' => ['id' => 'type', 'value' => 'Calculated'],
+// 'Type of information' => ['id' => 'value_type', 'value' => 'Numeric (float)']
+// ]
+// ]
+// ],
[
[
'names' => [
@@ -1510,7 +1496,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Custom multiplier', 'parameter_1' => 'abc']
],
- 'details' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -1524,7 +1510,8 @@ class testMassUpdateItems extends CWebTest{
['type' => 'Simple change'],
['type' => 'Simple change']
],
- 'details' => 'Only one change step is allowed.'
+ 'details' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within the '.
+ 'combinations of (type)=((9, 10)).'
]
],
[
@@ -1537,7 +1524,8 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'In range', 'parameter_1' => '8', 'parameter_2' => '-8']
],
- 'details' => 'Incorrect value for field "params": "min" value must be less than or equal to "max" value.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be less than or equal to '.
+ 'the value of parameter "/1/preprocessing/1/params/1".'
]
],
[
@@ -1550,7 +1538,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Check for error using regular expression', 'parameter_1' => 'test']
],
- 'details' => 'Incorrect value for field "params": second parameter is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.'
]
],
[
@@ -1564,7 +1552,8 @@ class testMassUpdateItems extends CWebTest{
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'details' => 'Only one throttling step is allowed.'
+ 'details' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within the '.
+ 'combinations of (type)=((19, 20)).'
]
],
[
@@ -1577,7 +1566,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Regular expression']
],
- 'details' => 'Incorrect value for field "params": first parameter is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -1595,7 +1584,7 @@ class testMassUpdateItems extends CWebTest{
'error_handler' => 'Set error to'
]
],
- 'details' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/error_handler_params": cannot be empty.'
]
],
[
diff --git a/ui/tests/selenium/data/data_test.sql b/ui/tests/selenium/data/data_test.sql
index a7b29c76302..3f583a56344 100644
--- a/ui/tests/selenium/data/data_test.sql
+++ b/ui/tests/selenium/data/data_test.sql
@@ -511,18 +511,18 @@ INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params,
INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15082, 15001, 0, 'testInheritanceDiscoveryRule5', 'discovery-rule-inheritance5', 3600, 4, '', '', 1, 15000, 15081, '', '');
-- testInheritanceItemPrototype.SimpleUpdate and testInheritanceItemPrototype.SimpleCreate
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15021, 15000, 0, 'itemDiscovery' , 'item-discovery-prototype', '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15022, 15000, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15023, 15000, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15024, 15000, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15025, 15000, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15095, 15000, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing' , 30, 3,'', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15026, 15001, 0, 'itemDiscovery' , 'item-discovery-prototype', '30s', 3, '', '', 2, 15000, 15021, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15027, 15001, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1' , '30s', 3, '', '', 2, 15000, 15022, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15028, 15001, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2' , '30s', 3, '', '', 2, 15000, 15023, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15029, 15001, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3' , '30s', 3, '', '', 2, 15000, 15024, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15030, 15001, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4' , '30s', 3, '', '', 2, 15000, 15025, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15096, 15001, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing' , '30s', 3, '', '', 2, 15000, 15095, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15021, 15000, 0, 'itemDiscovery' , 'item-discovery-prototype[{#KEY}]', '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15022, 15000, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15023, 15000, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15024, 15000, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15025, 15000, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15095, 15000, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing[{#KEY}]' , 30, 3,'', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15026, 15001, 0, 'itemDiscovery' , 'item-discovery-prototype[{#KEY}]', '30s', 3, '', '', 2, 15000, 15021, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15027, 15001, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15022, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15028, 15001, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15023, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15029, 15001, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15024, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15030, 15001, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15025, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15096, 15001, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing[{#KEY}]', '30s', 3, '', '', 2, 15000, 15095, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15021, 15021, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15022, 15022, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15023, 15023, 15011);
@@ -536,8 +536,8 @@ INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (1503
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15031, 15095, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15032, 15096, 15016);
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15083, 15002, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5' , '30s', 3, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15084, 15001, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5' , '30s', 3, '', '', 2, 15000, 15083, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15083, 15002, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5[{#KEY}]', '30s', 3, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15084, 15001, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5[{#KEY}]', '30s', 3, '', '', 2, 15000, 15083, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15083, 15083, 15081);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15084, 15084, 15082);
@@ -797,17 +797,17 @@ INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params,
INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormDiscoveryRule', 'discovery-rule-form', 40001, 4, 133800, 1, 50, '', '', 40011, '', '');
-- testFormItemPrototype.SimpleUpdate
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype1', 'item-prototype-form1', 40001, 3, 23800, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype1', 'item-prototype-form1[{#KEY}]', 40001, 3, 23800, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (501, 23800, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype2', 'item-prototype-form2', 40001, 3, 23801, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype2', 'item-prototype-form2[{#KEY}]', 40001, 3, 23801, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (502, 23801, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype3', 'item-prototype-form3', 40001, 3, 23802, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype3', 'item-prototype-form3[{#KEY}]', 40001, 3, 23802, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (503, 23802, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype4', 'item-prototype-form4', 40001, 3, 23803, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype4', 'item-prototype-form4[{#KEY}]', 40001, 3, 23803, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (504, 23803, 133800);
-- testFormTriggerPrototype.SimpleCreate
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemReuse', 'item-prototype-reuse', 40001, 3, 23804, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemReuse', 'item-prototype-reuse[{#KEY}]', 40001, 3, 23804, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (505, 23804, 133800);
-- testFormTriggerPrototype.SimpleUpdate
@@ -904,51 +904,51 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (50002, 50001, 4)
INSERT INTO hosts_templates (hosttemplateid, hostid, templateid) VALUES (50000, 50001, 50002);
INSERT INTO hosts_templates (hosttemplateid, hostid, templateid) VALUES (50002, 50000, 50002);
INSERT INTO interface (type, ip, dns, useip, port, main, hostid, interfaceid) VALUES (1, '127.0.7.1', '', '1', '10071', '1', 50001, 50015);
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400080,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 First,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400090,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 First]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400100,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 First]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400110,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 First,Web ZBX6663 First Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400120,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 First,Web ZBX6663 First Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400130,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 First,Web ZBX6663 First Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400140,9,50002,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400150,9,50002,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400160,9,50002,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400170,9,50002,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400180,9,50002,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400190,9,50002,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400200,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400210,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',400150,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400220,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400230,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400240,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400250,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400260,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400270,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',0400150,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400280,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400290,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400300,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400310,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400320,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663,,bps]','60s','30s','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400330,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400340,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400350,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663,Web ZBX6663 Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400360,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663,Web ZBX6663 Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400370,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663,Web ZBX6663 Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400380,0,50002,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400390,0,50001,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400400,0,50000,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400410,0,50000,'Item ZBX6663 First','item-ZBX6663-first','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400420,0,50001,'Item ZBX6663','item-ZBX6663','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400430,0,50001,'DiscoveryRule ZBX6663','drule-zbx6663','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400450,0,50002,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400460,0,50001,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400470,0,50000,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400480,0,50002,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400490,0,50001,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400500,0,50000,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400510,0,50000,'DiscoveryRule ZBX6663 First','drule-zbx6663-first','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400520,0,50001,'ItemProto ZBX6663 HSecond','item-proto-zbx6663-hsecond','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400540,0,50000,'ItemProto ZBX6663 TSecond','item-proto-zbx6663-tsecond','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400080,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 First,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400090,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 First]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400100,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 First]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400110,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 First,Web ZBX6663 First Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400120,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 First,Web ZBX6663 First Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400130,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 First,Web ZBX6663 First Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400140,9,50002,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400150,9,50002,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400160,9,50002,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400170,9,50002,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400180,9,50002,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400190,9,50002,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400200,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400210,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',400150,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400220,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400230,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400240,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400250,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400260,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400270,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',0400150,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400280,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400290,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400300,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400310,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400320,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663,,bps]','60s','30s','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400330,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400340,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400350,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663,Web ZBX6663 Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400360,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663,Web ZBX6663 Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400370,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663,Web ZBX6663 Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400380,0,50002,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400390,0,50001,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400400,0,50000,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400410,0,50000,'Item ZBX6663 First','item-ZBX6663-first','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400420,0,50001,'Item ZBX6663','item-ZBX6663','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400430,0,50001,'DiscoveryRule ZBX6663','drule-zbx6663','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400450,0,50002,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400460,0,50001,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400470,0,50000,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400480,0,50002,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400490,0,50001,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400500,0,50000,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400510,0,50000,'DiscoveryRule ZBX6663 First','drule-zbx6663-first','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'3600','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400520,0,50001,'ItemProto ZBX6663 HSecond','item-proto-zbx6663-hsecond[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400540,0,50000,'ItemProto ZBX6663 TSecond','item-proto-zbx6663-tsecond[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (507,400480,400450,'',0,0);
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (508,400490,400460,'',0,0);
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (509,400500,400470,'',0,0);
@@ -1004,21 +1004,37 @@ INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, req
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (100, 100, 'Web ZBX6663 Second Step', 1, 'Web ZBX6663 Second Url', 15, '', '', '');
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (101, 101, 'Web ZBX6663 Second Step', 1, 'Web ZBX6663 Second Url', 15, '', '', '');
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (102, 102, 'Web ZBX6663 Step', 1, 'Web ZBX6663 Url', 15, '', '', '');
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (922,98,40008,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (923,98,40009,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (924,98,40010,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (925,99,40014,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (926,99,40015,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (927,99,40016,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (928,100,40020,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (929,100,40021,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (930,100,40022,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (931,101,40026,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (932,101,40027,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (933,101,40028,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (934,102,40032,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (935,102,40033,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (936,102,40034,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (922,98,400080,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (923,98,400090,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (924,98,400100,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (925,99,400140,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (926,99,400150,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (927,99,400160,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (928,100,400200,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (929,100,400210,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (930,100,400220,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (931,101,400260,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (932,101,400270,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (933,101,400280,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (934,102,400320,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (935,102,400330,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (936,102,400340,4);
+
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (922,98,400110,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (923,98,400120,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (924,98,400130,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (925,99,400170,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (926,99,400180,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (927,99,400190,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (928,100,400230,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (929,100,400240,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (930,100,400250,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (931,101,400290,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (932,101,400300,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (933,101,400310,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (934,102,400350,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (935,102,400360,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (936,102,400370,2);
-- testZBX6648.eventsFilter
INSERT INTO hstgrp (groupid,name,type) VALUES (50000,'ZBX6648 Group No Hosts',0);
diff --git a/ui/tests/selenium/items/testFormItem.php b/ui/tests/selenium/items/testFormItem.php
index 416dee5306f..d23477c7214 100644
--- a/ui/tests/selenium/items/testFormItem.php
+++ b/ui/tests/selenium/items/testFormItem.php
@@ -979,7 +979,7 @@ class testFormItem extends CLegacyWebTest {
'key' => 'vfs.file.cksum[/sbin/shutdown]',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
+ 'An item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
]
]
],
@@ -1014,7 +1014,7 @@ class testFormItem extends CLegacyWebTest {
'delay' => 0,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
]
],
@@ -1040,7 +1040,7 @@ class testFormItem extends CLegacyWebTest {
'delay' => 86401,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.'
+ 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
]
],
@@ -1150,7 +1150,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1189,7 +1189,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": must have at least one interval greater than 0.'
]
]
],
@@ -1220,7 +1220,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1259,7 +1259,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1288,7 +1288,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1306,7 +1306,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1322,7 +1322,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1338,7 +1338,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1422,7 +1422,7 @@ class testFormItem extends CLegacyWebTest {
'history' => ' ',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": cannot be empty'
]
]
],
@@ -1435,7 +1435,7 @@ class testFormItem extends CLegacyWebTest {
'history' => 3599,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1448,7 +1448,7 @@ class testFormItem extends CLegacyWebTest {
'history' => 788400001,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1461,7 +1461,7 @@ class testFormItem extends CLegacyWebTest {
'history' => '-1',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1474,7 +1474,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => ' ',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": cannot be empty.'
]
]
],
@@ -1487,7 +1487,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => '-1',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1500,7 +1500,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => 788400001,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1513,7 +1513,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => 86399,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1705,7 +1705,7 @@ class testFormItem extends CLegacyWebTest {
'key' => 'item-ipmi-agent-error',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "ipmi_sensor": cannot be empty.'
+ 'Invalid parameter "/1/ipmi_sensor": cannot be empty.'
]
]
],
@@ -2024,6 +2024,7 @@ class testFormItem extends CLegacyWebTest {
}
}
if (isset($data['formCheck'])) {
+ $this->page->waitUntilReady();
$this->zbxTestClickXpath("//form[@name='items']//a[text()='$name']");
$this->zbxTestWaitUntilElementVisible(WebDriverBy::id('name'));
$this->zbxTestAssertElementValue('name', $name);
diff --git a/ui/tests/selenium/items/testFormItemHttpAgent.php b/ui/tests/selenium/items/testFormItemHttpAgent.php
index a00b80eaa0a..231aae8a507 100644
--- a/ui/tests/selenium/items/testFormItemHttpAgent.php
+++ b/ui/tests/selenium/items/testFormItemHttpAgent.php
@@ -423,7 +423,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -440,7 +440,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -456,7 +456,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -473,7 +473,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -490,7 +490,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -507,7 +507,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -523,7 +523,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -538,7 +538,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'JSON data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -553,7 +553,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'JSON data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": JSON is expected.'
]
]
],
@@ -567,7 +567,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": XML is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -582,7 +582,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found'
+ 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found'
]
]
],
@@ -597,7 +597,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": (73) expected \'>\''
+ 'Invalid parameter "/1/posts": (73) expected \'>\''
]
]
],
@@ -612,7 +612,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Incorrect value "*" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
],
@@ -626,7 +626,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Incorrect value "test" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
]
@@ -667,7 +667,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => '', 'value' => 'admin', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -678,7 +678,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['value' => 'admin']
],
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -689,7 +689,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => 'user update', 'value' => '', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -699,7 +699,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => '', 'value' => 'admin update', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -710,7 +710,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['value' => 'admin']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -721,7 +721,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => 'user']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -730,7 +730,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
[
'request_type' => 'JSON data',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -738,7 +738,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
[
'request_type' => 'XML data',
'error_details' => [
- 'Invalid parameter "posts": XML is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -749,7 +749,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'request_type' => 'XML data',
'error_details' => [
- 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found'
+ 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]
]
],
@@ -760,7 +760,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'Required status codes' => '*'
],
'error_details' => [
- 'Incorrect value "*" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
],
@@ -770,7 +770,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'Required status codes' => 'test'
],
'error_details' => [
- 'Incorrect value "test" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
]
diff --git a/ui/tests/selenium/items/testFormItemPrototype.php b/ui/tests/selenium/items/testFormItemPrototype.php
index 2f9f6d42974..48ee510589b 100644
--- a/ui/tests/selenium/items/testFormItemPrototype.php
+++ b/ui/tests/selenium/items/testFormItemPrototype.php
@@ -77,7 +77,7 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'host' => 'Simple form test host',
- 'key' => 'item-prototype-form1'
+ 'key' => 'item-prototype-form1[{#KEY}]'
]
],
[
@@ -280,13 +280,13 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'template' => 'Inheritance test template',
- 'key' => 'item-prototype-test1'
+ 'key' => 'item-prototype-test1[{#KEY}]'
]
],
[
[
'host' => 'Template inheritance test host',
- 'key' => 'item-prototype-test1'
+ 'key' => 'item-prototype-test1[{#KEY}]'
]
],
[
@@ -484,7 +484,7 @@ class testFormItemPrototype extends CLegacyWebTest {
[
'host' => 'Template inheritance test host',
'hostTemplate' => 'Inheritance test template',
- 'key' => 'item-prototype-preprocessing',
+ 'key' => 'item-prototype-preprocessing[{#KEY}]',
'preprocessing' => true
]
]
@@ -514,7 +514,6 @@ class testFormItemPrototype extends CLegacyWebTest {
' AND key_='.zbx_dbstr($data['key'])
);
$template_info = DBfetch($dbResult);
-
$this->assertNotEquals($template_info, null);
$itemid = $template_info['itemid'];
@@ -1074,17 +1073,18 @@ class testFormItemPrototype extends CLegacyWebTest {
// Returns create data
public static function create() {
return [
+ // #0
[
[
'expected' => TEST_GOOD,
'name' => 'Checksum of $1',
- 'key' => 'vfs.file.cksum[/sbin/shutdown]',
+ 'key' => 'vfs.file.cksum[/sbin/shutdown,{#KEY}]',
'dbName' => 'Checksum of $1',
'dbCheck' => true,
'formCheck' =>true
]
],
- // Duplicate item
+ // #1
[
[
'expected' => TEST_BAD,
@@ -1092,22 +1092,34 @@ class testFormItemPrototype extends CLegacyWebTest {
'key' => 'vfs.file.cksum[/sbin/shutdown]',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
+ 'Invalid parameter "/1/key_": must contain at least one low-level discovery macro.'
+ ]
+ ]
+ ],
+ // #2 Duplicate item
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'name' => 'Checksum of $1',
+ 'key' => 'vfs.file.cksum[/sbin/shutdown,{#KEY}]',
+ 'error_msg' => 'Cannot add item prototype',
+ 'errors' => [
+ 'An item prototype with key "vfs.file.cksum[/sbin/shutdown,{#KEY}]" already exists on host'
]
]
],
- // Item name is missing
+ // #3 Item name is missing
[
[
'expected' => TEST_BAD,
- 'key' =>'item-name-missing',
+ 'key' =>'item-name-missing[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "Name": cannot be empty.'
]
]
],
- // Item key is missing
+ // #4 Item key is missing
[
[
'expected' => TEST_BAD,
@@ -1118,25 +1130,25 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Empty timedelay
+ // #5 Empty timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => 0,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
]
],
- // Incorrect timedelay
+ // #6 Incorrect timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => '-30',
'error_msg' => 'Page received incorrect data',
'errors' => [
@@ -1144,25 +1156,25 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect timedelay
+ // #7 Incorrect timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => 86401,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.'
+ 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
]
],
- // Empty time flex period
+ // #8 Empty time flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '']
],
@@ -1172,12 +1184,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #9 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-11,00:00-24:00']
],
@@ -1187,12 +1199,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #10 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-25:00', 'instantCheck' => true]
],
@@ -1202,12 +1214,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #11 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,24:00-00:00']
],
@@ -1217,12 +1229,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #12 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00;2,00:00-24:00']
],
@@ -1232,12 +1244,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Multiple flex periods
+ // #13 Multiple flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '2,00:00-24:00'],
@@ -1246,12 +1258,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #14 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '2,00:00-24:00'],
@@ -1263,16 +1275,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #15 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex1',
- 'key' => 'item-flex-delay1',
+ 'key' => 'item-flex-delay1[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '2,00:00-24:00'],
@@ -1284,12 +1296,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #16 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00'],
@@ -1302,16 +1314,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": must have at least one interval greater than 0.'
]
]
],
- // Delay combined with flex periods
+ // #17 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex2',
- 'key' => 'item-flex-delay2',
+ 'key' => 'item-flex-delay2[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
@@ -1321,78 +1333,78 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // Delay combined with flex periods
+ // #18 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #19 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay3',
+ 'key' => 'item-flex-delay3[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '6-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #20 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay4',
+ 'key' => 'item-flex-delay4[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #21 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #22 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay5',
+ 'key' => 'item-flex-delay5[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #23 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00'],
@@ -1401,16 +1413,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #24 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '6-7,00:00-24:00'],
@@ -1419,48 +1431,48 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #25 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #26 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #27 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay6',
+ 'key' => 'item-flex-delay6[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00', 'remove' => true],
['flexDelay' => 0, 'flexTime' => '2,00:00-24:00', 'remove' => true],
@@ -1479,24 +1491,24 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #28 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay7',
+ 'key' => 'item-flex-delay7[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00', 'remove' => true],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #29 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex Check',
- 'key' => 'item-flex-delay8',
+ 'key' => 'item-flex-delay8[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00', 'remove' => true],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00', 'remove' => true],
@@ -1507,12 +1519,12 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // Seven flexfields - save OK
+ // #30 Seven flexfields - save OK
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex-maximum save OK',
- 'key' => 'item-flex-maximum-save',
+ 'key' => 'item-flex-maximum-save[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
@@ -1526,367 +1538,366 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // History
+ // #31 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-empty',
+ 'key' => 'item-history-empty[{#KEY}]',
'history' => ' ',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": cannot be empty.'
]
]
],
- // History
+ // #32 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => 3599,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // History
+ // #33 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => 788400001,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // History
+ // #34 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => '-1',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // Trends
+ // #35 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-empty',
+ 'key' => 'item-trends-empty[{#KEY}]',
'trends' => ' ',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": cannot be empty.'
]
]
],
- // Trends
+ // #36 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => '-1',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
- // Trends
+ // #37 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => 86399,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
- // Trends
+ // #38 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => 788400001,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
+ // #39
[
[
'expected' => TEST_GOOD,
'name' => '!@#$%^&*()_+-=[]{};:"|,./<>?',
- 'key' => 'item-symbols-test',
+ 'key' => 'item-symbols-test[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #40
[
[
'expected' => TEST_GOOD,
'name' => 'itemSimple',
- 'key' => 'key-template-simple',
+ 'key' => 'key-template-simple[{#KEY}]',
'formCheck' => true,
'dbCheck' => true
]
],
+ // #41
[
[
'expected' => TEST_GOOD,
'name' => 'itemName',
- 'key' => 'key-template-item',
+ 'key' => 'key-template-item[{#KEY}]',
'formCheck' => true
]
],
+ //#42
[
[
'expected' => TEST_GOOD,
'name' => 'itemTrigger',
- 'key' => 'key-template-trigger',
+ 'key' => 'key-template-trigger[{#KEY}]',
'formCheck' => true,
'dbCheck' => true,
'remove' => true
]
],
+ // #43
[
[
'expected' => TEST_GOOD,
'name' => 'itemRemove',
- 'key' => 'key-template-remove',
+ 'key' => 'key-template-remove[{#KEY}]',
'formCheck' => true,
'dbCheck' => true,
'remove' => true]
],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'itemInheritance',
- 'key' => 'test-item-reuse',
- 'error_msg' => 'Cannot add item prototype',
- 'errors' => [
- 'Item with key "test-item-reuse" already exists on "Simple form test host".'
- ]
- ]
- ],
- // List of all item types
+ // #44 List of all item types
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent',
'name' => 'Zabbix agent',
- 'key' => 'item-zabbix-agent',
+ 'key' => 'item-zabbix-agent[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
- // Update and custom intervals are hidden if item key is mqtt.get
+ // #45 Update and custom intervals are hidden if item key is mqtt.get
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent (active)',
'name' => 'Zabbix agent (active) mqtt',
- 'key' => 'mqtt.get[0]',
+ 'key' => 'mqtt.get[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #46
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent (active)',
'name' => 'Zabbix agent (active)',
- 'key' => 'item-zabbix-agent-active',
+ 'key' => 'item-zabbix-agent-active[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #47
[
[
'expected' => TEST_GOOD,
'type' => 'Simple check',
'name' => 'Simple check',
- 'key' => 'item-simple-check',
+ 'key' => 'item-simple-check[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #48
[
[
'expected' => TEST_GOOD,
'type' => 'SNMP agent',
'name' => 'SNMP agent',
- 'key' => 'item-snmp-agent',
+ 'key' => 'item-snmp-agent[{#KEY}]',
'snmp_oid' => '[IF-MIB::]ifInOctets.1',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #49
[
[
'expected' => TEST_GOOD,
'type' => 'SNMP trap',
'name' => 'SNMP trap',
- 'key' => 'snmptrap.fallback',
+ 'key' => 'snmptrap.fallback[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #50
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix internal',
'name' => 'Zabbix internal',
- 'key' => 'item-zabbix-internal',
+ 'key' => 'item-zabbix-internal[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #51
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper',
- 'key' => 'item-zabbix-trapper',
+ 'key' => 'item-zabbix-trapper[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #52
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper with macro in allowed hosts field',
- 'key' => 'item-zabbix-trapper-macro',
+ 'key' => 'item-zabbix-trapper-macro[{#KEY}]',
'allowed_hosts' => '{$TEST}',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #53
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper with macro and ip in allowed hosts field',
- 'key' => 'item-zabbix-trapper-macro-ip',
+ 'key' => 'item-zabbix-trapper-macro-ip[{#KEY}]',
'allowed_hosts' => '{$MACRO},127.0.0.1',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #54
[
[
'expected' => TEST_GOOD,
'type' => 'External check',
'name' => 'External check',
- 'key' => 'item-external-check',
+ 'key' => 'item-external-check[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #55
[
[
'expected' => TEST_GOOD,
'type' => 'Database monitor',
'name' => 'Database monitor',
- 'key' => 'item-database-monitor',
+ 'key' => 'item-database-monitor[{#KEY}]',
'params_ap' => 'SELECT * FROM items',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #56
[
[
'expected' => TEST_GOOD,
'type' => 'IPMI agent',
'name' => 'IPMI agent',
- 'key' => 'item-ipmi-agent',
+ 'key' => 'item-ipmi-agent[{#KEY}]',
'ipmi_sensor' => 'ipmi_sensor',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #57
[
[
'expected' => TEST_GOOD,
'type' => 'IPMI agent',
'name' => 'IPMI agent with spaces',
- 'key' => 'item-ipmi-agent-spaces',
+ 'key' => 'item-ipmi-agent-spaces[{#KEY}]',
'ipmi_sensor' => ' ipmi_sensor ',
'dbCheck' => true,
'formCheck' => true
]
],
- // IPMI sensor is optional if item key is ipmi.get
- [
- [
- 'expected' => TEST_GOOD,
- 'type' => 'IPMI agent',
- 'name' => 'IPMI agent with ipmi.get',
- 'key' => 'ipmi.get',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
+ // #58
[
[
'expected' => TEST_GOOD,
'type' => 'SSH agent',
'name' => 'SSH agent',
- 'key' => 'item-ssh-agent',
+ 'key' => 'item-ssh-agent[{#KEY}]',
'username' => 'zabbix',
'params_es' => 'executed script',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #59
[
[
'expected' => TEST_GOOD,
'type' => 'TELNET agent',
'name' => 'TELNET agent',
- 'key' => 'item-telnet-agent',
+ 'key' => 'item-telnet-agent[{#KEY}]',
'username' => 'zabbix',
'params_es' => 'executed script',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #60
[
[
'expected' => TEST_BAD,
'type' => 'IPMI agent',
'name' => 'IPMI agent error',
- 'key' => 'item-ipmi-agent-error',
+ 'key' => 'item-ipmi-agent-error[{#KEY}]',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "ipmi_sensor": cannot be empty.'
+ 'Invalid parameter "/1/ipmi_sensor": cannot be empty.'
]
]
],
+ // #61
[
[
'expected' => TEST_BAD,
'type' => 'SSH agent',
'name' => 'SSH agent error',
- 'key' => 'item-ssh-agent-error',
+ 'key' => 'item-ssh-agent-error[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "User name": cannot be empty.',
@@ -1894,12 +1905,13 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
+ // #62
[
[
'expected' => TEST_BAD,
'type' => 'TELNET agent',
'name' => 'TELNET agent error',
- 'key' => 'item-telnet-agent-error',
+ 'key' => 'item-telnet-agent-error[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "User name": cannot be empty.',
@@ -1907,42 +1919,45 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
+ // #63
[
[
'expected' => TEST_GOOD,
'type' => 'JMX agent',
'name' => 'JMX agent',
- 'key' => 'proto-jmx-agent',
+ 'key' => 'proto-jmx-agent[{#KEY}]',
'dbCheck' => true,
'formCheck' => true,
'remove' => true
]
],
+ // #64
[
[
'expected' => TEST_GOOD,
'type' => 'Calculated',
'name' => 'Calculated',
- 'key' => 'item-calculated',
+ 'key' => 'item-calculated[{#KEY}]',
'params_f' => '"formula"',
'dbCheck' => true,
'formCheck' => true,
'remove' => true
]
],
+ // #65
[
[
'expected' => TEST_BAD,
'type' => 'Calculated',
'name' => 'Calculated',
- 'key' => 'item-calculated',
+ 'key' => 'item-calculated[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "Formula": cannot be empty.'
]
]
],
- // Empty SQL query
+ // #66 Empty SQL query
[
[
'expected' => TEST_BAD,
@@ -1954,7 +1969,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #67 Default
[
[
'expected' => TEST_BAD,
@@ -1967,7 +1982,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #68 Default
[
[
'expected' => TEST_BAD,
@@ -1981,7 +1996,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #69 Default
[
[
'expected' => TEST_BAD,
@@ -1995,7 +2010,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #70 Default
[
[
'expected' => TEST_BAD,
diff --git a/ui/tests/selenium/items/testFormTestItem.php b/ui/tests/selenium/items/testFormTestItem.php
index dc663970428..4ca6d8e5812 100644
--- a/ui/tests/selenium/items/testFormTestItem.php
+++ b/ui/tests/selenium/items/testFormTestItem.php
@@ -24,6 +24,8 @@ require_once dirname(__FILE__).'/../common/testItemTest.php';
/**
* "Test item" function tests.
*
+ * @dataSource Proxies
+ *
* @backup items
*/
class testFormTestItem extends testItemTest{
diff --git a/ui/tests/selenium/items/testFormTestItemPrototype.php b/ui/tests/selenium/items/testFormTestItemPrototype.php
index cf15739313e..bd114a4655b 100644
--- a/ui/tests/selenium/items/testFormTestItemPrototype.php
+++ b/ui/tests/selenium/items/testFormTestItemPrototype.php
@@ -24,6 +24,8 @@ require_once dirname(__FILE__).'/../common/testItemTest.php';
/**
* "Test item prototype" function tests.
*
+ * @dataSource Proxies
+ *
* @backup items
*/
class testFormTestItemPrototype extends testItemTest {
@@ -57,7 +59,7 @@ class testFormTestItemPrototype extends testItemTest {
* @depends testFormTestItemPrototype_CheckButtonStateHost
*/
public function testFormTestItemPrototype_TestItemHost($data) {
- $this->checkTestItem($data, true, self::HOST_LLD_ID, null, true);
+ $this->checkTestItem($data, true, self::HOST_LLD_ID, null, false);
}
/**
@@ -67,7 +69,7 @@ class testFormTestItemPrototype extends testItemTest {
*
* @depends testFormTestItemPrototype_CheckButtonStateTemplate
*/
- public function ttestFormTestItemPrototype_TestItemTemplate($data) {
- $this->checkTestItem($data, false, self::TEMPLATE_LLD_ID, null, true);
+ public function testFormTestItemPrototype_TestItemTemplate($data) {
+ $this->checkTestItem($data, false, self::TEMPLATE_LLD_ID, null, false);
}
}
diff --git a/ui/tests/selenium/items/testInheritanceItem.php b/ui/tests/selenium/items/testInheritanceItem.php
index 667e1582233..7eb539763e2 100644
--- a/ui/tests/selenium/items/testInheritanceItem.php
+++ b/ui/tests/selenium/items/testInheritanceItem.php
@@ -75,7 +75,8 @@ class testInheritanceItem extends CLegacyWebTest {
'name' => 'itemInheritance',
'key' => 'key-item-inheritance',
'errors' => [
- 'Item "key-item-inheritance" already exists on "Template inheritance test host", inherited from another template.'
+ 'Cannot inherit LLD rule with key "key-item-inheritance" of template "Inheritance test template" '.
+ 'to host "Template inheritance test host", because a discovered item with the same key already exists.'
]
]
]
diff --git a/ui/tests/selenium/items/testInheritanceItemPrototype.php b/ui/tests/selenium/items/testInheritanceItemPrototype.php
index 19e0689ac59..95046f2464c 100644
--- a/ui/tests/selenium/items/testInheritanceItemPrototype.php
+++ b/ui/tests/selenium/items/testInheritanceItemPrototype.php
@@ -69,16 +69,18 @@ class testInheritanceItemPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'name' => 'testInheritanceItemPrototype6',
- 'key' => 'item-prototype-test6'
+ 'key' => 'item-prototype-test6[{#KEY}]'
]
],
[
[
'expected' => TEST_BAD,
'name' => 'testInheritanceItemPrototype5',
- 'key' => 'item-prototype-test5',
+ 'key' => 'item-prototype-test5[{#KEY}]',
'errors' => [
- 'Item prototype "item-prototype-test5" already exists on "Template inheritance test host", inherited from another template'
+ 'Cannot inherit item prototype with key "item-prototype-test5[{#KEY}]" of template '.
+ '"Inheritance test template" to host "Template inheritance test host", because an item '.
+ 'with the same key is already inherited from template "Inheritance test template 2".'
]
]
]
diff --git a/ui/tests/selenium/items/testItemTypeSelection.php b/ui/tests/selenium/items/testItemTypeSelection.php
index 363f5a9960f..adb7d7140e6 100644
--- a/ui/tests/selenium/items/testItemTypeSelection.php
+++ b/ui/tests/selenium/items/testItemTypeSelection.php
@@ -49,7 +49,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Character',
- 'Key' => 'agent.hostmetadata'
+ 'Key' => 'agent.hostmetadata[{#KEY}]'
],
'type' => 'Character'
]
@@ -58,7 +58,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Numeric unsigned',
- 'Key' => 'agent.ping'
+ 'Key' => 'agent.ping[{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -68,7 +68,7 @@ class testItemTypeSelection extends CWebTest {
'fields' => [
'Name' => 'Numeric float',
- 'Key' => 'net.udp.service.perf[service]'
+ 'Key' => 'net.udp.service.perf[service,{#KEY}]'
],
'type' => 'Numeric (float)'
]
@@ -78,7 +78,7 @@ class testItemTypeSelection extends CWebTest {
'fields' => [
'Type' => 'Zabbix agent (active)',
'Name' => 'Log',
- 'Key' => 'eventlog[name]'
+ 'Key' => 'eventlog[name,{#KEY}]'
],
'type' => 'Log'
]
@@ -87,7 +87,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Log',
- 'Key' => 'eventlog[agent]'
+ 'Key' => 'eventlog[agent,{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -96,7 +96,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Text',
- 'Key' => 'net.if.discovery'
+ 'Key' => 'net.if.discovery[{#KEY}]'
],
'type' => 'Text'
]
@@ -105,7 +105,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Custom key',
- 'Key' => 'custom.key'
+ 'Key' => 'custom.key[{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -114,7 +114,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Custom key 2',
- 'Key' => 'custom.key2',
+ 'Key' => 'custom.key2[{#KEY}]',
'Type of information' => 'Text'
],
'type' => 'Text',
@@ -125,7 +125,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Test Info Hint',
- 'Key' => 'net.if.list',
+ 'Key' => 'net.if.list[{#KEY}]',
'Type of information' => 'Log'
],
'hint' => true,
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php b/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
index 0fbfb79a2d0..e3ac95f33a1 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
@@ -53,7 +53,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
['type' => 'Prometheus pattern', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -71,7 +71,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
'parameter_3' => '{#LABELNAME}'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -85,7 +85,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
]
]);
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php b/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
index f3eceba132d..e71735e60f1 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
@@ -48,7 +48,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus LLD macro in parameters 1',
- 'Key' => 'parameters-macro-1'
+ 'Key' => 'parameters-macro-1[{#KEY}]'
],
'preprocessing' => [
[
@@ -64,7 +64,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus LLD macro in parameters 2',
- 'Key' => 'parameters-macro-2'
+ 'Key' => 'parameters-macro-2[{#KEY}]'
],
'preprocessing' => [
[
@@ -80,7 +80,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometheus to JSON LLD macro in parameter 1 ',
- 'Key' => 'json-parameter-macro-1'
+ 'Key' => 'json-parameter-macro-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
@@ -92,7 +92,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus to Json LLD macro in parameter 2',
- 'Key' => 'json-parameters-macro-2'
+ 'Key' => 'json-parameters-macro-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="{#LABELVALUE}"}']
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php b/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
index fed4923de2b..08ac9a46626 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
@@ -262,7 +262,8 @@ class testFormPreprocessingLowLevelDiscovery extends testFormPreprocessing {
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
]
]);
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php b/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
index 63c15dfdabb..9a326dbb1f0 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
@@ -162,9 +162,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Left trim', 'parameter_1' => ''],
['type' => 'XML XPath', 'parameter_1' => ''],
['type' => 'JSONPath', 'parameter_1' => ''],
- ['type' => 'Custom multiplier', 'parameter_1' => ''],
['type' => 'JavaScript', 'parameter_1' => ''],
- ['type' => 'In range', 'parameter_1' => '', 'parameter_2' => ''],
['type' => 'Matches regular expression', 'parameter_1' => ''],
['type' => 'Does not match regular expression', 'parameter_1' => ''],
['type' => 'Check for error in JSON', 'parameter_1' => ''],
@@ -173,7 +171,27 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => ''],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'value']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'preprocessing' => [
+ ['type' => 'In range', 'parameter_1' => '', 'parameter_2' => '']
+
+ ],
+ 'error' => 'Invalid parameter "/1/params": cannot be empty.'
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'preprocessing' => [
+ ['type' => 'Custom multiplier', 'parameter_1' => '']
+
+ ],
+ 'error' => 'Invalid parameter "/1/params/1": a floating point value is expected.'
]
],
[
@@ -184,7 +202,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Check for error using regular expression', 'parameter_1' => 'path']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/2": cannot be empty.'
]
],
[
@@ -195,7 +213,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Regular expression', 'parameter_1' => '', 'parameter_2' => '1'],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'label', 'parameter_3' => 'label']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
]
]
];
@@ -331,7 +349,8 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Simple change'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -341,7 +360,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((19, 20)).'
]
],
[
@@ -352,7 +371,7 @@ class testFormPreprocessingTest extends CWebTest {
'parameter_3' => 'label_name'],
['type' => 'Prometheus to JSON', 'parameter_1' => '']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((22, 23)).'
]
],
[
@@ -376,7 +395,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => ''],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'value']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
]
],
[
@@ -386,7 +405,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Regular expression', 'parameter_1' => 'expr', 'parameter_2' => 'output'],
['type' => 'Trim', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/2/params/1": cannot be empty.'
]
],
[
@@ -399,7 +418,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'JavaScript', 'parameter_1' => 'Script'],
['type' => 'Check for error in XML', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/5/params/1": cannot be empty.'
]
]
];
@@ -528,15 +547,6 @@ class testFormPreprocessingTest extends CWebTest {
case TEST_BAD:
$message = $dialog->query('tag:output')->asMessage()->waitUntilPresent()->one();
$this->assertTrue($message->isBad());
-
- // Workaround for single step which has different message.
- $this->assertTrue($message->hasLine(
- ($id !== null && $data['preprocessing'][$id]['type'] === 'Discard unchanged with heartbeat')
- ? 'Invalid parameter "params":'
- : $data['error']
- )
- );
-
$dialog->close();
break;
diff --git a/ui/tests/selenium/testFormTriggerPrototype.php b/ui/tests/selenium/testFormTriggerPrototype.php
index 192cd641248..be6c96362f5 100644
--- a/ui/tests/selenium/testFormTriggerPrototype.php
+++ b/ui/tests/selenium/testFormTriggerPrototype.php
@@ -527,77 +527,77 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_sysUptime',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '1234567890',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'a?aa+',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '}aa]a{',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '-aaa=%',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa,;:',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa><.',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa*&_',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa#@!',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '([)$^',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_generalCheck',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<5',
'type' => true,
'comments' => 'Trigger status (expression) is recalculated every time Zabbix server receives new value, if this value is part of this expression. If time based functions are used in the expression, it is recalculated every 30 seconds by a zabbix timer process. ',
'url_name' => 'Trigger context menu name for trigger URL.',
@@ -610,7 +610,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_CheckUrl',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<5',
'url_name' => 'MyTrigger: menu name',
'url' => 'index.php'
]
@@ -653,10 +653,10 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0 or {#MACRO}',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0 or {#MACRO}',
'constructor' => [
'text' => ['A or B', 'A', 'B'],
- 'elements' => ['expr_0_53', 'expr_58_65']
+ 'elements' => ['expr_0_61', 'expr_66_73']
]
]
],
@@ -812,7 +812,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
if (isset($data['expression'])) {
switch ($data['expression']) {
case 'default':
- $expression = 'last(/'.$this->host.'/'.$this->itemKey.',#1)=0';
+ $expression = 'last(/'.$this->host.'/'.$this->itemKey.'[{#KEY}],#1)=0';
$this->zbxTestInputType('expression', $expression);
break;
default:
diff --git a/ui/tests/selenium/testInheritanceTriggerPrototype.php b/ui/tests/selenium/testInheritanceTriggerPrototype.php
index 3d2e5a5ae82..84b151ce684 100644
--- a/ui/tests/selenium/testInheritanceTriggerPrototype.php
+++ b/ui/tests/selenium/testInheritanceTriggerPrototype.php
@@ -78,7 +78,7 @@ class testInheritanceTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'testInheritanceTriggerPrototype5',
- 'expression' => 'last(/Inheritance test template/item-discovery-prototype)<0'
+ 'expression' => 'last(/Inheritance test template/item-discovery-prototype[{#KEY}])<0'
]
],
[
diff --git a/ui/tests/selenium/testPageMassUpdateItemPrototypes.php b/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
index ab919b600aa..771140c9b07 100644
--- a/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
+++ b/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
@@ -62,8 +62,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'key_' => 'snmptrap[{#KEY1}]',
'type' => 17,
'value_type' => 0,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '3m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -72,8 +71,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'key_' => 'snmptrap[{#KEY2}]',
'type' => 17,
'value_type' => 1,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '4m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -136,7 +134,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '12_SSH_Agent',
- 'key_' => 'ssh.run[{#KEY}]',
+ 'key_' => 'ssh.run[{#KEY2}]',
'type' => 13,
'value_type' => 1,
'interfaceid' => self::AGENT_INTERFACE_ID,
@@ -169,7 +167,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '14_DB_Monitor',
- 'key_' => 'db.odbc.select[{#KEY}]',
+ 'key_' => 'db.odbc.select[{#KEY2}]',
'type' => 11,
'value_type' => 0,
'delay' => '90s',
@@ -223,7 +221,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '17_Script',
- 'key_' => 'script1',
+ 'key_' => 'script1[{#KEY}]',
'type' => 21,
'value_type' => 0,
'delay' => '15s',
@@ -234,7 +232,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '18_Script',
- 'key_' => 'script2',
+ 'key_' => 'script2[{#KEY}]',
'type' => 21,
'value_type' => 0,
'delay' => '14s',
@@ -276,6 +274,19 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'Discover' => ['id' => 'discover', 'value' => 'Yes']
]
]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'names' => [
+ '1_Item',
+ '2_Item'
+ ],
+ 'change' => [
+ 'Type' => ['id' => 'type', 'value' => 'Dependent item']
+ ],
+ 'details' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.'
+ ]
]
];
}
diff --git a/ui/tests/selenium/testPageMassUpdateItems.php b/ui/tests/selenium/testPageMassUpdateItems.php
index 2d2ef309a49..8cc622417fb 100644
--- a/ui/tests/selenium/testPageMassUpdateItems.php
+++ b/ui/tests/selenium/testPageMassUpdateItems.php
@@ -59,8 +59,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'key_' => 'snmptrap.fallback',
'type' => 17,
'value_type' => 0,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '3m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -68,8 +67,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'key_' => 'snmptrap[regexp]',
'type' => 17,
'value_type' => 1,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '4m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -157,7 +155,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
[
'hostid' => self::HOSTID,
'name' => '14_DB_Monitor',
- 'key_' => 'db.odbc.select',
+ 'key_' => 'db.odbc.select[]',
'type' => 11,
'value_type' => 0,
'delay' => '90s',
@@ -242,7 +240,8 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'change' => [
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
- 'Status' => ['id' => 'status', 'value' => 'Disabled']
+ 'Status' => ['id' => 'status', 'value' => 'Disabled'],
+ 'Update interval' => ['Delay' => '1m']
]
]
],
@@ -255,9 +254,23 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'change' => [
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
- 'Status' => ['id' => 'status', 'value' => 'Enabled']
+ 'Status' => ['id' => 'status', 'value' => 'Enabled'],
+ 'Update interval' => ['Delay' => '1m']
]
]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'names' => [
+ '1_Item',
+ '2_Item'
+ ],
+ 'change' => [
+ 'Type' => ['id' => 'type', 'value' => 'Dependent item']
+ ],
+ 'details' => 'Invalid parameter "/1/master_itemid": an item ID is expected.'
+ ]
]
];
}
diff --git a/ui/tests/selenium/testTemplateInheritance.php b/ui/tests/selenium/testTemplateInheritance.php
index adc66996cd9..ed9cb787136 100644
--- a/ui/tests/selenium/testTemplateInheritance.php
+++ b/ui/tests/selenium/testTemplateInheritance.php
@@ -83,8 +83,8 @@ class testTemplateInheritance extends CLegacyWebTest {
'testInheritance',
'key-item-inheritance',
[
- 'Item "key-item-inheritance" already exists on "Template inheritance test host", inherited from '.
- 'another template.'
+ 'Cannot inherit LLD rule with key "key-item-inheritance" of template "Inheritance test template" '.
+ 'to host "Template inheritance test host", because a discovered item with the same key already exists.'
]
],
// Item added to Template inheritance test host
@@ -346,7 +346,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create item prototype');
$this->zbxTestInputTypeWait('name', 'Test LLD item');
- $this->zbxTestInputType('key', 'test-lld-item');
+ $this->zbxTestInputType('key', 'test-lld-item[{#KEY}]');
$this->zbxTestDropdownSelect('type', 'Simple check');
$this->zbxTestDropdownSelect('value_type', 'Numeric (unsigned)');
$this->zbxTestInputType('units', 'units');
@@ -375,7 +375,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestClickLinkTextWait('Test LLD item');
$this->zbxTestAssertElementValue('name', 'Test LLD item');
- $this->zbxTestAssertElementValue('key', 'test-lld-item');
+ $this->zbxTestAssertElementValue('key', 'test-lld-item[{#KEY}]');
$this->zbxTestDropdownAssertSelected('type', 'Simple check');
$this->zbxTestDropdownAssertSelected('value_type', 'Numeric (unsigned)');
$this->zbxTestAssertElementValue('units', 'units');
@@ -407,7 +407,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create trigger prototype');
$this->zbxTestInputTypeByXpath("//input[@name='description']", 'Test LLD trigger');
- $this->zbxTestInputType('expression', 'last(/Inheritance test template/item-discovery-prototype,#1)=0');
+ $this->zbxTestInputType('expression', 'last(/Inheritance test template/item-discovery-prototype[{#KEY}],#1)=0');
$this->zbxTestCheckboxSelect('type_1');
$this->zbxTestInputType('comments', 'comments');
$this->zbxTestInputType('url', 'zabbix.php');
@@ -436,7 +436,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestWaitUntilElementVisible(WebDriverBy::id('description'));
$getName = $this->zbxTestGetValue("//input[@name='description']");
$this->assertEquals($getName, 'Test LLD trigger');
- $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/item-discovery-prototype,#1)=0');
+ $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/item-discovery-prototype[{#KEY}],#1)=0');
$this->assertTrue($this->zbxTestCheckboxSelected('recovery_mode_0'));
$this->zbxTestAssertElementPresentXpath("//input[@id='recovery_mode_0'][@disabled]");
$this->zbxTestAssertElementText('//*[@name="comments"]', 'comments');
diff --git a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
index d861b817d56..ae62d19d55e 100644
--- a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
+++ b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
@@ -980,7 +980,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1030,7 +1029,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1080,7 +1078,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [
[
'tag' => 'Application',
@@ -1385,7 +1382,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1435,7 +1431,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1485,7 +1480,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [
[
'tag' => 'Application',
@@ -3196,7 +3190,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3298,7 +3291,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3400,7 +3392,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3994,7 +3985,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -4096,7 +4086,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -4198,7 +4187,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
diff --git a/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php b/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
index 66ec545f8fe..6181a0e832d 100644
--- a/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
@@ -64,6 +64,13 @@ class CPrometheusOutputParserTest extends TestCase {
'match' => '{#LLD}'
]
],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
// partial success
[
'label1=', 0, [],
@@ -86,6 +93,27 @@ class CPrometheusOutputParserTest extends TestCase {
'match' => 'l1'
]
],
+ [
+ '{$MACRO} label1 ', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{$MACRO}'
+ ]
+ ],
+ [
+ '{#LLD_MACRO} label1 ', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{#LLD_MACRO}'
+ ]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} label1 ', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
// fail
[
'', 0, [],
@@ -137,6 +165,20 @@ class CPrometheusOutputParserTest extends TestCase {
'rc' => CParser::PARSE_FAIL,
'match' => ''
]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
]
];
}
diff --git a/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php b/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
index 7dab378ef7d..80be830b43e 100644
--- a/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
@@ -233,6 +233,62 @@ class CPrometheusPatternParserTest extends TestCase {
]
],
[
+ '{label1="{$MACRO}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO} abc {$MACRO2}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO} abc {$MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO}"}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO} abc {$MACRO2}"}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO} abc {$MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO}"}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}'
+ ]
+ ],
+ [
'{label1="value1"}==666', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
@@ -344,6 +400,34 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => '{#LLD1}{{#LLD2}="value1"}=={#LLD3}'
]
],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
+ [
+ 'metric == {{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric == {{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
+ [
+ 'metric{{#LLD_MACRO}="value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric{{#LLD_MACRO}="value1"} == Nan'
+ ]
+ ],
+ [
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} = "value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} = "value1"} == Nan'
+ ]
+ ],
// Label value can by anything, no user macro enabling flag is required.
[
'{label1="{$M}"}', 0, [],
@@ -382,6 +466,13 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => '{label1!~"value1"}'
]
],
+ [
+ '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}'
+ ]
+ ],
// partial success
[
'metric=1.e1', 0, [],
@@ -559,12 +650,36 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => 'metric'
]
],
- // Functional macros are not supported.
+ // Incorrect syntax of functional LLD macros in label.
[
- '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ 'metric {{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")="value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => 'metric'
+ ]
+ ],
+ // Multiple macros for metric.
+ [
+ '{#LLD_MACRO}{#LLD_MACRO2}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{#LLD_MACRO}'
+ ]
+ ],
+ // Multiple macros for label.
+ [
+ 'metric{{#LLD_MACRO}{#LLD_MACRO2} = "value"}', 0, ['lldmacros' => true],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
- 'match' => '{#LLD} {label1="value1"}'
+ 'match' => 'metric'
+ ]
+ ],
+ // Multiple macros for value.
+ [
+ 'metric{label = "value"} == {#LLD_MACRO}{#LLD_MACRO2}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => 'metric{label = "value"} == {#LLD_MACRO}'
]
],
// fail
@@ -759,6 +874,34 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => ''
]
],
+ [
+ '{label1={$MACRO}}==""', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={$MACRO}}==""', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={#LLD_MACRO}}==""', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={#LLD_MACRO}}==""', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
// LLD macros are not enabled.
[
'{#LLD}{label1="value1"}=={#LLD}', 0, ['usermacros' => true],
@@ -767,9 +910,25 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => ''
]
],
- // Functional macros are not supported.
+ // Multiple LLD macros in label.
+ [
+ '{{#LLD_MACRO}{#LLD_MACRO2}="value1"}==Inf', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ // Incorrect syntax of functional LLD macros in metric.
+ [
+ '{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1") {label1="value1"} == value', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ // Incorrect syntax of functional LLD macros in metric.
[
- '{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ '{{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")="value1"} == value', 0, ['lldmacros' => true],
[
'rc' => CParser::PARSE_FAIL,
'match' => ''
diff --git a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
index 832673f4b25..022d88c3591 100644
--- a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
@@ -960,12 +960,6 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/int32_ranges": invalid range expression.'
],
[
- ['type' => API_INT32_RANGES],
- '{$MACRO},30-40',
- '/1/int32_ranges',
- 'Invalid parameter "/1/int32_ranges": invalid range expression.'
- ],
- [
['type' => API_INT32_RANGES, 'in' => '0:50'],
'10-20,30-40',
'/1/int32_ranges',
@@ -978,6 +972,54 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/int32_ranges": value must be one of 20-30.'
],
[
+ ['type' => API_INT32_RANGES],
+ '{$MACRO}',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": invalid range expression.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/int32_ranges',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO1}-{$MACRO2}',
+ '/1/int32_ranges',
+ '{$MACRO1}-{$MACRO2}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO, 'in' => '20:30'],
+ '{$MACRO}-20,30-40',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": value must be one of 20-30.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO, 'in' => '20:40'],
+ '{$MACRO}-20,30-40',
+ '/1/int32_ranges',
+ '{$MACRO}-20,30-40'
+ ],
+ [
+ ['type' => API_INT32_RANGES],
+ '{#LLD}',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": invalid range expression.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/int32_ranges',
+ '{#LLD}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD1}-{#LLD2}',
+ '/1/int32_ranges',
+ '{#LLD1}-{#LLD2}'
+ ],
+ [
['type' => API_UINT64],
0,
'/1/int',
@@ -1278,6 +1320,228 @@ class CApiInputValidatorTest extends TestCase {
0.23E+11
],
[
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ '0.5',
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ 0.5,
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ 1,
+ '/1/float',
+ 1.0
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ 1,
+ '/1/float',
+ 1.0
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '1.3',
+ '/1/float',
+ 1.3
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '0.5',
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '3.5',
+ '/1/float',
+ 3.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '0',
+ '/1/float',
+ 'Invalid parameter "/1/float": value must be within the range of 0.5-3.5.'
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ 4.5,
+ '/1/float',
+ 'Invalid parameter "/1/float": value must be within the range of 0.5-3.5.'
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '-123,1,1.5,2'],
+ '-123',
+ '/1/float',
+ -123.0
+ ],
+ [
+ ['type' => API_FLOAT],
+ '{$MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT],
+ '{#LLD_MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ 108.108,
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '108.108',
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/float',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO: with context}',
+ '/1/float',
+ '{$MACRO: with context}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ 'Simple string',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{#LLD_MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ 108.108,
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '108.108',
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}',
+ '/1/float',
+ '{#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/float',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ 'Simple string',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{$MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '107',
+ 'to' => '108'
+ ],
+ '/',
+ [
+ 'from' => 107.0,
+ 'to' => 108.0
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108',
+ 'to' => '108.000108'
+ ],
+ '/',
+ [
+ 'from' => 108.0,
+ 'to' => 108.000108
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108',
+ 'to' => '108'
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => 108.0,
+ 'to' => 108.0
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108.001',
+ 'to' => '108.0'
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
['type' => API_FLOATS],
[0, 1],
'/output',
@@ -1446,6 +1710,66 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/id": a number is expected.'
],
[
+ ['type' => API_ID, 'in' => '0'],
+ 0,
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '0',
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '000000',
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ true,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ null,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ [],
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 0.0,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 1.23E+11,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 108,
+ '/1/id',
+ 'Invalid parameter "/1/id": value must be 0.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '108',
+ '/1/id',
+ 'Invalid parameter "/1/id": value must be 0.'
+ ],
+ [
['type' => API_BOOLEAN],
true,
'/1/createMissing',
@@ -1589,6 +1913,34 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_OBJECT, 'fields' => [
+ 'host' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8]
+ ]],
+ [
+ 'host' => 'Zabbix server'
+ ],
+ '/',
+ [
+ 'host' => 'Zabbix server'
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'host' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8]
+ ]],
+ [
+ 'host' => 'Zabbix server',
+ 'name' => 'Zabbix server'
+ ],
+ '/',
+ [
+ 'host' => 'Zabbix server',
+ 'name' => 'Zabbix server'
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
'host' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
]],
@@ -3330,6 +3682,12 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_REGEX],
+ 'Server: nginx\/(.+(?<!\r))',
+ '/1/expression',
+ 'Server: nginx\/(.+(?<!\r))'
+ ],
+ [
+ ['type' => API_REGEX],
'/',
'/1/expression',
'/'
@@ -4320,6 +4678,42 @@ class CApiInputValidatorTest extends TestCase {
'192.168.3.5,192.168.6.1-240'
],
[
+ ['type' => API_IP_RANGES],
+ '{$MACRO}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{$MACRO}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/ip_range',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_IP_RANGES],
+ '{HOST.IP}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{HOST.IP}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => true],
+ '{HOST.IP}',
+ '/1/ip_range',
+ '{HOST.IP}'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => ['{HOST.IP}']],
+ '{HOST.DNS}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{HOST.DNS}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => ['{HOST.IP}']],
+ '{HOST.IP}',
+ '/1/ip_range',
+ '{HOST.IP}'
+ ],
+ [
['type' => API_DNS],
'',
'/1/dns',
@@ -4706,6 +5100,247 @@ class CApiInputValidatorTest extends TestCase {
''
],
[
+ ['type' => API_JSON],
+ null,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ true,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ [],
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ 123,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '123',
+ '/1/json',
+ '123'
+ ],
+ [
+ ['type' => API_JSON],
+ '',
+ '/1/json',
+ ''
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/json',
+ 'Invalid parameter "/1/json": cannot be empty.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{}',
+ '/1/json',
+ '{}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": "value"}',
+ '/1/json',
+ '{"key": "value"}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": false}',
+ '/1/json',
+ '{"key": false}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": null}',
+ '/1/json',
+ '{"key": null}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": NaN}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'length' => 15],
+ '{"key": "value"}',
+ '/1/json',
+ 'Invalid parameter "/1/json": value is too long.'
+ ],
+ [
+ ['type' => API_JSON],
+ 'abc',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": value}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": 123}',
+ '/1/json',
+ '{"key": 123}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{$MACRO}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/json',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {$MACRO}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_USER_MACRO],
+ '{"key": {$MACRO}}',
+ '/1/json',
+ '{"key": {$MACRO}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{#LLD}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/json',
+ '{#LLD}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {#LLD}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_LLD_MACRO],
+ '{"key": {#LLD}}',
+ '/1/json',
+ '{"key": {#LLD}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{HOST.IP}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{HOST.IP}',
+ '/1/json',
+ '{HOST.IP}'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{HOST.IP2}',
+ '/1/json',
+ '{HOST.IP2}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {HOST.IP}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{"key1": {HOST.IP1}, "key2": {HOST.IP2}}',
+ '/1/json',
+ '{"key1": {HOST.IP1}, "key2": {HOST.IP2}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '[]',
+ '/1/json',
+ '[]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[]]',
+ '/1/json',
+ '[[]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[], []]',
+ '/1/json',
+ '[[], []]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[1]]',
+ '/1/json',
+ '[[1]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[1, 2, 3]',
+ '/1/json',
+ '[1, 2, 3]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[true]]',
+ '/1/json',
+ '[[true]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[null]]',
+ '/1/json',
+ '[[null]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '{null: "value"}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '[{"key": "value"}]',
+ '/1/json',
+ '[{"key": "value"}]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[{"key": "value"}, {"key": "value"}]',
+ '/1/json',
+ '[{"key": "value"}, {"key": "value"}]'
+ ],
+ [
+ ['type' => API_JSON],
+ '["key": "value"]',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+
+ [
['type' => API_JSONRPC_PARAMS],
[],
'/params',
@@ -5543,6 +6178,1654 @@ class CApiInputValidatorTest extends TestCase {
['type' => 3, 'name' => 2, 'value' => ['1', 2.5, '3', '4', '1']],
'/',
['type' => [3], 'name' => [2], 'value' => ['1', 2.5, '3', '4', '1']]
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key',
+ '/1/item_key',
+ 'key'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key',
+ '/1/item_key',
+ 'super.key'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[]',
+ '/1/item_key',
+ 'super.key[]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[0]',
+ '/1/item_key',
+ 'super.key[0]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[a, b, c]',
+ '/1/item_key',
+ 'super.key[a, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{HOST.HOST}]',
+ '/1/item_key',
+ 'super.key[{HOST.HOST}]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[abc{HOST.HOST}def]',
+ '/1/item_key',
+ 'super.key[abc{HOST.HOST}def]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{#LLD_MACRO}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{#LLD_MACRO1}abc{#LLD_MACRO2}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO1}abc{#LLD_MACRO2}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key["{#LLD_MACRO}", "b b", "c\""]',
+ '/1/item_key',
+ 'super.key["{#LLD_MACRO}", "b b", "c\""]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key["{#LLD_MACRO1} {#LLD_MACRO2}", b, c]',
+ '/1/item_key',
+ 'super.key["{#LLD_MACRO1} {#LLD_MACRO2}", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{#LLD_MACRO}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c{#LLD_MACRO}d]',
+ '/1/item_key',
+ 'super.key[a, b, c{#LLD_MACRO}d]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{{#LLD_MACRO}.fmtnum(2)}, b, c]',
+ '/1/item_key',
+ 'super.key[{{#LLD_MACRO}.fmtnum(2)}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c{{#LLD_MACRO}.fmtnum(2)}d]',
+ '/1/item_key',
+ 'super.key[a, b, c{{#LLD_MACRO}.fmtnum(2)}d]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}", b, c]',
+ '/1/item_key',
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, "{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}"]',
+ '/1/item_key',
+ 'super.key[a, b, "{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}"]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)", b, c]',
+ '/1/item_key',
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ null,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 123,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ true,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ [],
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": cannot be empty.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'length' => 2],
+ 'key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": value is too long.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '/key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "/key".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '{#LLD_MACRO}',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "{#LLD_MACRO}".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[{#LLD_MACRO}]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[{#LLD_MACRO}]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[key".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[key]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[key]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[a',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[a, "]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, {$MACRO}, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{#LLD_MACRO, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "+)", \1)}, b, c]".'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ null,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ 'abc',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ 123,
+ '/1/item_delay',
+ '123'
+ ],
+ 'Delay less than zero' => [
+ ['type' => API_ITEM_DELAY],
+ -1,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m',
+ '/1/item_delay',
+ '1m'
+ ],
+ [
+ ['type' => API_ITEM_DELAY, 'length' => 2],
+ '10m',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": value is too long.'
+ ],
+ 'Zero delay without intervals' => [
+ ['type' => API_ITEM_DELAY],
+ 0,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": cannot be equal to zero without custom intervals.'
+ ],
+ 'Nonsense in flexible interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/abc,10:00-18:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": incorrect syntax near ";1m/abc,10:00-18:00".'
+ ],
+ 'Nonsense in flexible period' => [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/1-7,abc',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": incorrect syntax near ";1m/1-7,abc".'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/1-5,10:00-18:00',
+ '/1/item_delay',
+ '0;1m/1-5,10:00-18:00'
+ ],
+ 'Delay too big' => [
+ ['type' => API_ITEM_DELAY],
+ SEC_PER_DAY + 1,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": value must be one of 0-'.SEC_PER_DAY.'.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m;30s/1-7,10:00-18:00',
+ '/1/item_delay',
+ '1m;30s/1-7,10:00-18:00'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m;h9m/30',
+ '/1/item_delay',
+ '1m;h9m/30'
+ ],
+ 'No user macro flag' => [
+ ['type' => API_ITEM_DELAY],
+ '{$MACRO}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'User macro allowed' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/item_delay',
+ '{$MACRO}'
+ ],
+ 'User macro allowed, but LLD macro entered' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '{#LLD}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'No LLD macro flag' => [
+ ['type' => API_ITEM_DELAY],
+ '{#LLD}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'LLD macro allowed, but user macro entered' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_LLD_MACRO],
+ '{$MACRO}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/item_delay',
+ '{#LLD}'
+ ],
+ 'User macros in a flexible interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;{$M}/{$M}',
+ '/1/item_delay',
+ '0;{$M}/{$M}'
+ ],
+ 'User macros in a scheduled interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;{$M}',
+ '/1/item_delay',
+ '0;{$M}'
+ ],
+ 'Zero delay and blocking zero-interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;50s/1-6,09:00-18:00;0/1-6,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Zero delay and multiple combined blocking zero-intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;50s/1-6,09:00-18:00;0/1-3,00:00-24:00;0/4-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-convertible due to macro in period' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;50s/1-6,09:00-18:00;0/1-5,00:00-24:00;0/{$M}',
+ '/1/item_delay',
+ '0;50s/1-6,09:00-18:00;0/1-5,00:00-24:00;0/{$M}'
+ ],
+ 'Non-zero delay, but whole week consists of blocking interval' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-zero delay, but whole week combined of blocking intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-4,00:00-24:00;0/3-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Macro used, but delay and intervals are all zero-blocking' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-6,09:00-12:00;0/{$M}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have at least one interval greater than 0.'
+ ],
+ 'Macro in Period, but zero-week block' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-7,00:00-24:00;1/{$M}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Macro in Interval, but zero-week block' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-7,00:00-24:00;{$M}/1-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-zero delay, macro in Interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '1m;{$M}/1-4,00:00-24:00;0/3-7,00:00-23:00',
+ '/1/item_delay',
+ '1m;{$M}/1-4,00:00-24:00;0/3-7,00:00-23:00'
+ ],
+ 'Polling overlapped by zero-interval as a whole' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;5m/2-4,00:00-24:00;0/1-7,00:00-23:57',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling chunk overlapped by zero-interval as a whole, but has another active interval' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/2-4,00:00-24:00;0/1-5,00:00-24:00;50s/6,09:30-12:00',
+ '/1/item_delay',
+ '1m;50s/2-4,00:00-24:00;0/1-5,00:00-24:00;50s/6,09:30-12:00'
+ ],
+ 'Overlap by zero-interval, but polling window available before' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/2-6,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/2-6,00:00-24:00'
+ ],
+ 'Zero-interval, but polling active outside' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/2-3,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/2-3,00:00-24:00'
+ ],
+ 'Polling window available between side-overlapping zero chunks' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/1-3,00:00-24:00;0/5-6,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/1-3,00:00-24:00;0/5-6,00:00-24:00'
+ ],
+ 'Polling window available too small for interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;2h/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling window available equal to interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;90m/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ '0;90m/1-6,09:00-12:00;0/1-6,09:00-10:30'
+ ],
+ 'Polling window just less than available interval (90m-1s)' => [
+ ['type' => API_ITEM_DELAY],
+ '0;5399/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ '0;5399/1-6,09:00-12:00;0/1-6,09:00-10:30'
+ ],
+ 'Polling available via delay, before zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/2-7,00:00-24:00;0/1,00:05-24:00',
+ '/1/item_delay',
+ '5m;0/2-7,00:00-24:00;0/1,00:05-24:00'
+ ],
+ 'Polling available via delay, after zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-6,00:00-24:00;0/7,00:00-23:55',
+ '/1/item_delay',
+ '5m;0/1-6,00:00-24:00;0/7,00:00-23:55'
+ ],
+ 'Polling available via delay, between zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-3,00:00-24:00;0/4,00:05-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-3,00:00-24:00;0/4,00:05-24:00;0/5-7,00:00-24:00'
+ ],
+ 'Polling possible via shorter of intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:45-24:00',
+ '/1/item_delay',
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:45-24:00'
+ ],
+ 'Polling impossible via shorter of intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:44-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling possible via shorter of intervals, full window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-11:00',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-11:00'
+ ],
+ 'Polling possible via shorter of intervals, end of window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:50-11:00',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:50-11:00'
+ ],
+ 'Polling possible via shorter of intervals, start of window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:10',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:10'
+ ],
+ 'Polling possible via shorter of intervals, with overlap of cut-off longer one' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:40-10:50',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:40-10:50'
+ ],
+ 'Interval shorter than period allowed' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:09',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": update interval "10m" is longer than period "1-7,10:00-10:09".'
+ ],
+ 'No window for delay' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Window for delay OK' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:05-24:00'
+ ],
+ 'Window for smaller delay OK' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ '1m;0/1-7,00:05-24:00'
+ ],
+ 'Polling via delay blocked by active flexible interval' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:05-24:00;10m/1-7,00:04-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling via delay not blocked by flexible intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:10-24:00;10m/1-7,05:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:10-24:00;10m/1-7,05:00-24:00'
+ ],
+ 'Delay does not fit due to several flexible intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-7,00:00-00:01;0/1-7,00:10-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits between blocking zero intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-3,00:00-24:00;0/4,00:00-23:50;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '10m;0/1-3,00:00-24:00;0/4,00:00-23:50;0/5-7,00:00-24:00'
+ ],
+ 'Delay fits between blocking mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-3,00:00-24:00;0/4,00:00-23:45;10m/4,23:50-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-3,00:00-24:00;0/4,00:00-23:45;10m/4,23:50-24:00;0/5-7,00:00-24:00'
+ ],
+ 'Delay does not fit between blocking mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '6m;0/1-3,00:00-24:00;0/4,00:00-23:45;5m/4,23:50-23:55;0/4,23:54-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits after blocking zero intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00',
+ '/1/item_delay',
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00'
+ ],
+ 'Delay does not fit with after mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00;20m/7,00:00-23:55;0/7,00:00-23:49',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits at start of mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00'
+ ],
+ 'Delay does not fit at start of mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '8m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ [
+ ['type' => API_XML],
+ null,
+ '/1/xml',
+ 'Invalid parameter "/1/xml": a character string is expected.'
+ ],
+ [
+ ['type' => API_XML],
+ 123,
+ '/1/xml',
+ 'Invalid parameter "/1/xml": a character string is expected.'
+ ],
+ [
+ ['type' => API_XML],
+ '',
+ '/1/xml',
+ ''
+ ],
+ [
+ ['type' => API_XML, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": cannot be empty.'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 39].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>'
+ ],
+ [
+ ['type' => API_XML, 'length' => 10],
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": value is too long.'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="123">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="123">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop=string>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (39) AttValue: " or \' expected [Line: 1 | Column: 50].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (38) Unescaped \'<\' not allowed in attributes values [Line: 1 | Column: 63].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="<">value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (38) Unescaped \'<\' not allowed in attributes values [Line: 1 | Column: 51].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="&lt;">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="&lt;">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script></node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (76) Opening and ending tag mismatch: script line 1 and node [Line: 1 | Column: 60].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script/></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node><script/></node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script /></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node><script /></node>'
+ ],
+ 'Opening and ending tag mismatch' => [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node></a></node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (76) Opening and ending tag mismatch: node line 1 and a [Line: 1 | Column: 49].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>/></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>/></node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>/&gt;</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>/&gt;</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>"</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>"</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>&quot;</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>&quot;</node>'
+ ],
+
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1.0',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1.08',
+ '/1/params',
+ '1.08'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER], 'length' => 2],
+ '1.08',
+ '/1/params',
+ 'Invalid parameter "/1/params": value is too long.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ 'abc',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ "1.08\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ "1.08\n1.08",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_RTRIM]],
+ ' ")',
+ '/1/params',
+ ' ")'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_LTRIM]],
+ ' "(',
+ '/1/params',
+ ' "('
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_TRIM]],
+ ' "()',
+ '/1/params',
+ ' "()'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_RTRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_LTRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_TRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$\n\\1",
+ '/1/params',
+ "^[a-z]$\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "{\$MACRO}\n\\1",
+ '/1/params',
+ "{\$MACRO}\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "{\#LLD_MACRO}\n\\1",
+ '/1/params',
+ "{\#LLD_MACRO}\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z$\n\\1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$\n\\1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => ZBX_PREPROC_REGSUB],
+ 'params' => ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['field' => 'type']]
+ ]],
+ [
+ 'type' => ZBX_PREPROC_REGSUB,
+ 'params' => "^[a-z]$\n\\1"
+ ],
+ '/',
+ [
+ 'type' => ZBX_PREPROC_REGSUB,
+ 'params' => "^[a-z]$\n\\1"
+ ]
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ 'number(/document/item/@attribute)',
+ '/1/params',
+ 'number(/document/item/@attribute)'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ "number(/document/item/@attribute)\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ '$.object.name',
+ '/1/params',
+ '$.object.name'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ "$.object.name\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n",
+ '/1/params',
+ "1\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "\n10",
+ '/1/params',
+ "\n10"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n10",
+ '/1/params',
+ "1\n10"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "10.01\n10",
+ '/1/params',
+ 'Invalid parameter "/1/params/2": cannot be less than or equal to the value of parameter "/1/params/1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "10.01",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n10\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ "^[a-z]$"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "@^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z$\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ "^[a-z]$"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "@^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z$\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ '$.object.error',
+ '/1/params',
+ '$.object.error'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ "$.object.error\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ 'number(/document/item/@error)',
+ '/1/params',
+ 'number(/document/item/@error)'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ "number(/document/item/@error)\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$\n\\1",
+ '/1/params',
+ "^[a-z]$\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z$\n\\1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$\n\\1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '30',
+ '/1/params',
+ '30'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400000', // 25 years
+ '/1/params',
+ '788400000'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '30s',
+ '/1/params',
+ '30s'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '10m',
+ '/1/params',
+ '10m'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1h',
+ '/1/params',
+ '1h'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1w',
+ '/1/params',
+ '1w'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1.08',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1M',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1y',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '-1',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '0',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400001',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400001',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '-1m',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '9126d', // 25 years and 1 day
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ "30\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ 'return true;',
+ '/1/params',
+ 'return true;'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ "let a = 'abc';\nreturn a;",
+ '/1/params',
+ "let a = 'abc';\nreturn a;"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue",
+ '/1/params',
+ "metric{label1=\"value1\"}\nvalue\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue\n",
+ '/1/params',
+ "metric{label1=\"value1\"}\nvalue\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "{\nvalue",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\nlabel1",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\nlabel1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'flags' => API_ALLOW_USER_MACRO, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n{\$MACRO}",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\n{\$MACRO}"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'flags' => API_ALLOW_LLD_MACRO, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n{#LLD_MACRO}",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\n{#LLD_MACRO}"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n-label1-",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\nlabel1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nsum",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nsum"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nmin",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nmin"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nmax",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nmax"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\navg",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\navg"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\ncount",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\ncount"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\ncook",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "sum", "min", "max", "avg", "count".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "sum", "min", "max", "avg", "count".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nmystery",
+ '/1/params',
+ 'Invalid parameter "/1/params/2": value must be one of "value", "label", "function".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "\nlabel\nlabel1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ '',
+ '/1/params',
+ ''
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ 'metric{label1="value1"} == 123',
+ '/1/params',
+ 'metric{label1="value1"} == 123'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ 'metric{%label1="value1"} == 123',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ "metric{label1=\"value1\"} == 123\nvalue",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n0",
+ '/1/params',
+ "\n\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\n0",
+ '/1/params',
+ ",\n\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n0",
+ '/1/params',
+ ",\n\"\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n1",
+ '/1/params',
+ ",\n\"\n1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "0", "1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n0\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n2",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "0", "1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc\n",
+ '/1/params',
+ "abc\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc",
+ '/1/params',
+ "abc\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc\ndef",
+ '/1/params',
+ "abc\ndef"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '',
+ '/1/prometheus_pattern',
+ ''
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'cpu_usage_system{cpu="cpu-total"}',
+ '/1/prometheus_pattern',
+ 'cpu_usage_system{cpu="cpu-total"}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{$MACRO}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ 'metric{{$MACRO}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{$MACRO}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{$MACRO}"}==123',
+ '/1/prometheus_pattern',
+ 'metric{label1="{$MACRO}"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ 'metric{label="value1"}=={$MACRO}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={$MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{#LLD_MACRO}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{{#LLD_MACRO}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{#LLD_MACRO}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{#LLD_MACRO}"}==123',
+ '/1/prometheus_pattern',
+ 'metric{label1="{#LLD_MACRO}"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{label="value1"}=={#LLD_MACRO}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{label="value1"}=={{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ null,
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 123,
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": cannot be empty.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{$MACRO}',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{#LLD_MACRO}',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{$MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'label1',
+ '/1/prometheus_label',
+ 'label1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'superLabel_1',
+ '/1/prometheus_label',
+ 'superLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '_superLabel_1',
+ '/1/prometheus_label',
+ '_superLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'SuperLabel_1',
+ '/1/prometheus_label',
+ 'SuperLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ null,
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 123,
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": cannot be empty.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '1_label',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'label}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{$MACRO}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/prometheus_label',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{#LLD_MACRO}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}',
+ '/1/prometheus_label',
+ '{#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{{#LLD_MACRO2}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
]
];
}
@@ -6186,6 +8469,315 @@ class CApiInputValidatorTest extends TestCase {
'/',
false,
'Invalid parameter "/3/value/5": value (1) already exists.'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '1', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '1', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1, 2)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '2', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1, 2)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [
+ ['type' => ['1', '2']],
+ ['type' => ['3']]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [
+ ['type' => ['1', '2']],
+ ['type' => ['3']]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((3)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'b']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'a']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'a']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'd']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test6', 'method' => 'b']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/6": only one object can exist within the combinations of (type, method)=((1, 2), (a, b)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'b']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a, b)).'
]
];
}