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>2020-08-07 11:40:32 +0300
committerVladimirs Maksimovs <vladimirs.maksimovs@zabbix.com>2020-08-07 11:40:32 +0300
commit106664e854a09412a7f350987db7a4760732932a (patch)
tree46afeff33f80e18b493b29c284c30bd2a168ab00
parenta5e24fa9e9b580cb1a4eba9fcecd1b14a411e5c7 (diff)
parent34f8fa0b5f32bc618c1baf0bb755057e92810219 (diff)
.......... [ZBXNEXT-82] updated to latest from master; no conflicts
-rw-r--r--ChangeLog.d/bugfix/ZBX-169981
-rw-r--r--ChangeLog.d/bugfix/ZBX-174681
-rw-r--r--ChangeLog.d/bugfix/ZBX-177851
-rw-r--r--ChangeLog.d/bugfix/ZBX-178151
-rw-r--r--ChangeLog.d/bugfix/ZBX-181061
-rw-r--r--ChangeLog.d/bugfix/ZBX-181321
-rw-r--r--ChangeLog.d/bugfix/ZBX-181491
-rw-r--r--ChangeLog.d/bugfix/ZBX-181691
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-27541
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-61001
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-61011
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-61021
-rw-r--r--ChangeLog.d/feature/ZBXNEXT-61031
-rw-r--r--build/mingw/Makefile4
-rw-r--r--configure.ac1
-rw-r--r--include/zbxtypes.h14
-rw-r--r--sass/stylesheets/sass/screen.scss83
-rw-r--r--src/go/cmd/zabbix_agent2/testrun_darwin.go1
-rw-r--r--src/go/cmd/zabbix_agent2/testrun_linux.go1
-rw-r--r--src/go/cmd/zabbix_agent2/testrun_windows.go1
-rw-r--r--src/go/internal/agent/resultcache/diskcache.go1
-rw-r--r--src/go/internal/agent/resultcache/resultcache.go4
-rw-r--r--src/go/pkg/zbxlib/checks_linux.go8
-rw-r--r--src/go/plugins/plugins_linux.go1
-rw-r--r--src/go/plugins/vfs/fs/fs.go29
-rw-r--r--src/go/plugins/vfs/fs/fs_nix.go162
-rw-r--r--src/go/plugins/vfs/fs/fs_windows.go19
-rw-r--r--src/go/plugins/zabbix/async/async_nix.go1
-rw-r--r--src/go/plugins/zabbix/sync/sync_nix.go3
-rw-r--r--src/zabbix_server/housekeeper/housekeeper.c2
-rw-r--r--templates/db/oracle_odbc/README.md77
-rw-r--r--templates/db/oracle_odbc/template_db_oracle_odbc.xml66
-rw-r--r--templates/db/postgresql/postgresql/pgsql.dbstat.sql2
-rw-r--r--ui/app/controllers/CControllerAuthenticationEdit.php4
-rw-r--r--ui/app/controllers/CControllerAuthenticationUpdate.php10
-rw-r--r--ui/app/controllers/CControllerExport.php120
-rw-r--r--ui/app/controllers/CControllerExportXml.php120
-rw-r--r--ui/app/views/administration.authentication.edit.php38
-rw-r--r--ui/app/views/administration.mediatype.list.php13
-rw-r--r--ui/app/views/administration.valuemap.list.php13
-rw-r--r--ui/app/views/layout.export.php (renamed from ui/app/views/layout.xml.php)2
-rw-r--r--ui/assets/styles/blue-theme.css49
-rw-r--r--ui/assets/styles/dark-theme.css49
-rw-r--r--ui/assets/styles/hc-dark.css49
-rw-r--r--ui/assets/styles/hc-light.css49
-rw-r--r--ui/host_discovery.php2
-rw-r--r--ui/hostgroups.php9
-rw-r--r--ui/hosts.php28
-rw-r--r--ui/include/classes/api/CAudit.php1
-rw-r--r--ui/include/classes/api/services/CConfiguration.php59
-rw-r--r--ui/include/classes/api/services/CHost.php32
-rw-r--r--ui/include/classes/export/CConfigurationExport.php17
-rw-r--r--ui/include/classes/export/CConfigurationExportBuilder.php16
-rw-r--r--ui/include/classes/export/writers/CExportWriterFactory.php33
-rw-r--r--ui/include/classes/export/writers/CJsonExportWriter.php8
-rw-r--r--ui/include/classes/export/writers/CYamlExportWriter.php37
-rw-r--r--ui/include/classes/html/CActionButtonList.php74
-rw-r--r--ui/include/classes/html/CButtonDropdown.php8
-rw-r--r--ui/include/classes/html/CButtonExport.php89
-rw-r--r--ui/include/classes/import/converters/CImportDataNormalizer.php (renamed from ui/include/classes/import/converters/CArrayKeysImportConverter.php)33
-rw-r--r--ui/include/classes/import/importers/CMapImporter.php2
-rw-r--r--ui/include/classes/import/readers/CImportReaderFactory.php17
-rw-r--r--ui/include/classes/import/readers/CYamlImportReader.php66
-rw-r--r--ui/include/classes/import/validators/C50XmlValidator.php14
-rw-r--r--ui/include/classes/import/validators/CXmlValidatorGeneral.php5
-rw-r--r--ui/include/classes/mvc/CRouter.php12
-rw-r--r--ui/include/classes/setup/CFrontendSetup.php41
-rw-r--r--ui/include/defines.inc.php13
-rw-r--r--ui/include/hosts.inc.php27
-rw-r--r--ui/include/views/configuration.host.discovery.list.php14
-rw-r--r--ui/include/views/configuration.host.list.php13
-rw-r--r--ui/include/views/configuration.template.list.php13
-rw-r--r--ui/include/views/js/conf.import.js.php2
-rw-r--r--ui/include/views/monitoring.screen.list.php13
-rw-r--r--ui/include/views/monitoring.sysmap.list.php13
-rw-r--r--ui/js/class.cnavtree.js12
-rw-r--r--ui/js/dashboard.grid.js7
-rw-r--r--ui/js/init.js3
-rw-r--r--ui/js/menupopup.js33
-rw-r--r--ui/tests/api_json/data/data_test.sql2
-rw-r--r--ui/tests/api_json/testConfiguration.php284
-rw-r--r--ui/tests/selenium/testFormAdministrationGeneralInstallation.php3
82 files changed, 1545 insertions, 519 deletions
diff --git a/ChangeLog.d/bugfix/ZBX-16998 b/ChangeLog.d/bugfix/ZBX-16998
new file mode 100644
index 00000000000..5d024706467
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-16998
@@ -0,0 +1 @@
+...G...... [ZBX-16998] improved agent binary compatibility by removing __thread attribute when not building agent2 (wiper)
diff --git a/ChangeLog.d/bugfix/ZBX-17468 b/ChangeLog.d/bugfix/ZBX-17468
new file mode 100644
index 00000000000..8d8bf29ec8d
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-17468
@@ -0,0 +1 @@
+A.F....... [ZBX-17468] fixed excess audit log record being added on host status update (talbergs)
diff --git a/ChangeLog.d/bugfix/ZBX-17785 b/ChangeLog.d/bugfix/ZBX-17785
new file mode 100644
index 00000000000..681bce76661
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-17785
@@ -0,0 +1 @@
+.........T [ZBX-17785] fixed dbstat query for postgresql v12 (afayantsev)
diff --git a/ChangeLog.d/bugfix/ZBX-17815 b/ChangeLog.d/bugfix/ZBX-17815
new file mode 100644
index 00000000000..842a6d42f6e
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-17815
@@ -0,0 +1 @@
+...G...... [ZBX-17815] fixed connection timeout when persistent buffer enabled (asestakovs)
diff --git a/ChangeLog.d/bugfix/ZBX-18106 b/ChangeLog.d/bugfix/ZBX-18106
new file mode 100644
index 00000000000..c9c626f6d55
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-18106
@@ -0,0 +1 @@
+..F....... [ZBX-18106] fixed map navigation tree widget copying (miks)
diff --git a/ChangeLog.d/bugfix/ZBX-18132 b/ChangeLog.d/bugfix/ZBX-18132
new file mode 100644
index 00000000000..7842e843c0a
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-18132
@@ -0,0 +1 @@
+..F....... [ZBX-18132] added OpenSSL extension check in frontend setup and SAML authentication form (Ivo)
diff --git a/ChangeLog.d/bugfix/ZBX-18149 b/ChangeLog.d/bugfix/ZBX-18149
new file mode 100644
index 00000000000..049a662219a
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-18149
@@ -0,0 +1 @@
+..F....... [ZBX-18149] fixed discovered hosts showing links to host prototypes which should not possible (Ivo)
diff --git a/ChangeLog.d/bugfix/ZBX-18169 b/ChangeLog.d/bugfix/ZBX-18169
new file mode 100644
index 00000000000..4fd9c09be49
--- /dev/null
+++ b/ChangeLog.d/bugfix/ZBX-18169
@@ -0,0 +1 @@
+........S. [ZBX-18169] fixed housekeeper not deleting history and trends for deleted items if storage period is not overridden (vso)
diff --git a/ChangeLog.d/feature/ZBXNEXT-2754 b/ChangeLog.d/feature/ZBXNEXT-2754
new file mode 100644
index 00000000000..0963176f107
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-2754
@@ -0,0 +1 @@
+A.F....... [ZBXNEXT-2754] added YAML as default import/export format (Ivo)
diff --git a/ChangeLog.d/feature/ZBXNEXT-6100 b/ChangeLog.d/feature/ZBXNEXT-6100
new file mode 100644
index 00000000000..351e281ba83
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-6100
@@ -0,0 +1 @@
+...G...... [ZBXNEXT-6100] add native linux vfs.fs.size[fs,<mode>] metric support to agent2 (esneiders)
diff --git a/ChangeLog.d/feature/ZBXNEXT-6101 b/ChangeLog.d/feature/ZBXNEXT-6101
new file mode 100644
index 00000000000..eb77395064c
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-6101
@@ -0,0 +1 @@
+...G...... [ZBXNEXT-6101] add native linux vfs.fs.inode[fs,<mode>] metric support to agent2 (esneiders)
diff --git a/ChangeLog.d/feature/ZBXNEXT-6102 b/ChangeLog.d/feature/ZBXNEXT-6102
new file mode 100644
index 00000000000..6b63f6fcc22
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-6102
@@ -0,0 +1 @@
+...G...... [ZBXNEXT-6102] add native linux vfs.fs.get metric support to agent2 (esneiders)
diff --git a/ChangeLog.d/feature/ZBXNEXT-6103 b/ChangeLog.d/feature/ZBXNEXT-6103
new file mode 100644
index 00000000000..c728b84b0c6
--- /dev/null
+++ b/ChangeLog.d/feature/ZBXNEXT-6103
@@ -0,0 +1 @@
+...G...... [ZBXNEXT-6103] add native linux vfs.fs.discovery metric support to agent2 (esneiders)
diff --git a/build/mingw/Makefile b/build/mingw/Makefile
index 972c5a8d182..dcb5fc33530 100644
--- a/build/mingw/Makefile
+++ b/build/mingw/Makefile
@@ -33,7 +33,9 @@ OBJS = \
$(OUTPUTDIR)\eventlog.o \
$(TARGETDIR)\resource.syso
-CFLAGS := $(CFLAGS) -O2 -g -DUNICODE -DHAVE_STDINT_H=1 -I$(TOPDIR)\build\win32\include -I$(TOPDIR)\build\mingw -I$(TOPDIR)\include
+CFLAGS := $(CFLAGS) -O2 -g -DUNICODE -DHAVE_STDINT_H=1 -DZBX_BUILD_AGENT2=1 -DHAVE_THREAD_LOCAL=1 \
+ -I$(TOPDIR)\build\win32\include -I$(TOPDIR)\build\mingw -I$(TOPDIR)\include
+
RFLAGS := $(RFLAGS) --input-format=rc -O coff
GOPATH := $(GOPATH);$(TOPDIR)\go
TAGS :=
diff --git a/configure.ac b/configure.ac
index 0fb70af4804..39a1d278dd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1734,6 +1734,7 @@ if test "x$agent2" = "xyes"; then
if test "x$GO" = "xno"; then
AC_MSG_ERROR([Unable to find "go" executable in path])
fi
+ AC_DEFINE(ZBX_BUILD_AGENT2,1,[Define to 1 if Agent2 is being built.])
fi
if test "x$java" = "xyes"; then
diff --git a/include/zbxtypes.h b/include/zbxtypes.h
index b64ca0dde44..0d3a5b421ed 100644
--- a/include/zbxtypes.h
+++ b/include/zbxtypes.h
@@ -20,12 +20,20 @@
#ifndef ZABBIX_TYPES_H
#define ZABBIX_TYPES_H
+#include "sysinc.h"
+
#if defined(_WINDOWS)
# define ZBX_THREAD_LOCAL __declspec(thread)
#else
-# if defined(HAVE_THREAD_LOCAL) && (defined(__GNUC__) || defined(__clang__) || defined(__MINGW32__))
-# define ZBX_THREAD_LOCAL __thread
-# else
+/* for non windows build thread local storage is required only for agent2 */
+# if defined(ZBX_BUILD_AGENT2)
+# if defined(HAVE_THREAD_LOCAL) && (defined(__GNUC__) || defined(__clang__) || defined(__MINGW32__))
+# define ZBX_THREAD_LOCAL __thread
+# else
+# error "C compiler is not compatible with agent2 assembly"
+# endif
+# endif
+# if !defined(ZBX_THREAD_LOCAL)
# define ZBX_THREAD_LOCAL
# endif
#endif
diff --git a/sass/stylesheets/sass/screen.scss b/sass/stylesheets/sass/screen.scss
index 72d94d43650..e705513e02e 100644
--- a/sass/stylesheets/sass/screen.scss
+++ b/sass/stylesheets/sass/screen.scss
@@ -7051,6 +7051,80 @@ table.preprocessing-test-results {
}
}
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px;
+
+ li {
+ display: inline-block;
+
+ button {
+ margin: 0 -1px 0 0;
+ border-radius: 0;
+ }
+
+ &:first-child button {
+ border-radius: 2px 0 0 2px;
+ }
+
+ &:last-child button {
+ border-radius: 0 2px 2px 0;
+ }
+
+ &:only-child button {
+ border-radius: 2px;
+ }
+ }
+}
+
+.btn-toggle-chevron {
+ position: relative;
+
+ &[aria-expanded="true"] {
+ @include btn-hover-active($btn-font-color, $btn-bg-color);
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid $btn-alt-font-color;
+ border-right: 1px solid $btn-alt-font-color;
+ transform: rotate(135deg) translate(-1px,1px);
+ transition: transform .3s;
+ }
+
+ &[disabled] {
+ &::after {
+ border-top-color: $btn-disabled-font-color;
+ border-right-color: $btn-disabled-font-color;
+ }
+ }
+
+ &:enabled {
+ &:active,
+ &[aria-expanded="true"] {
+ &::after {
+ transform: rotate(315deg) translate(-1px,1px);
+ }
+ }
+
+ &:hover,
+ &:focus,
+ &:active,
+ &[aria-expanded="true"] {
+ &::after {
+ border-top-color: $btn-font-color;
+ border-right-color: $btn-font-color;
+ }
+ }
+ }
+}
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -7068,12 +7142,17 @@ table.preprocessing-test-results {
height: 22px;
content: '';
}
+
+ &::after {
+ top: 6px;
+ right: 2px;
+ }
}
&::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/src/go/cmd/zabbix_agent2/testrun_darwin.go b/src/go/cmd/zabbix_agent2/testrun_darwin.go
index 399fcb7575a..d6deb160c94 100644
--- a/src/go/cmd/zabbix_agent2/testrun_darwin.go
+++ b/src/go/cmd/zabbix_agent2/testrun_darwin.go
@@ -57,6 +57,7 @@ func checkMetrics(s scheduler.Scheduler) {
`vfs.fs.size[/,free]`,
`vfs.fs.inode[/,free]`,
`vfs.fs.discovery`,
+ `vfs.fs.get`,
`vfs.dev.write[sda,operations]`,
`net.tcp.listen[80]`,
`net.udp.listen[68]`,
diff --git a/src/go/cmd/zabbix_agent2/testrun_linux.go b/src/go/cmd/zabbix_agent2/testrun_linux.go
index b329d762f0c..8fe3598f72e 100644
--- a/src/go/cmd/zabbix_agent2/testrun_linux.go
+++ b/src/go/cmd/zabbix_agent2/testrun_linux.go
@@ -59,6 +59,7 @@ func checkMetrics(s scheduler.Scheduler) {
`vfs.fs.size[/,free]`,
`vfs.fs.inode[/,free]`,
`vfs.fs.discovery`,
+ `vfs.fs.get`,
`vfs.dev.write[sda,operations]`,
`net.tcp.listen[80]`,
`net.udp.listen[68]`,
diff --git a/src/go/cmd/zabbix_agent2/testrun_windows.go b/src/go/cmd/zabbix_agent2/testrun_windows.go
index 72a98e874ee..73af9f86fee 100644
--- a/src/go/cmd/zabbix_agent2/testrun_windows.go
+++ b/src/go/cmd/zabbix_agent2/testrun_windows.go
@@ -55,6 +55,7 @@ func checkMetrics(s scheduler.Scheduler) {
`zabbix.stats[127.0.0.1,10051]`,
`vfs.fs.size[c:,free]`,
`vfs.fs.discovery`,
+ `vfs.fs.get`,
`net.tcp.listen[80]`,
`net.if.in[MS TCP Loopback interface,bytes]`,
`net.if.out[MS TCP Loopback interface,bytes]`,
diff --git a/src/go/internal/agent/resultcache/diskcache.go b/src/go/internal/agent/resultcache/diskcache.go
index 2768d07276b..076d27fdbbe 100644
--- a/src/go/internal/agent/resultcache/diskcache.go
+++ b/src/go/internal/agent/resultcache/diskcache.go
@@ -410,6 +410,7 @@ func (c *DiskCache) run() {
func (c *DiskCache) updateOptions(options *agent.AgentOptions) {
c.storagePeriod = int64(options.PersistentBufferPeriod)
+ c.timeout = options.Timeout
}
func (c *DiskCache) insertResultTable(table string) string {
diff --git a/src/go/internal/agent/resultcache/resultcache.go b/src/go/internal/agent/resultcache/resultcache.go
index 0e9c454a82a..9552cf0ab80 100644
--- a/src/go/internal/agent/resultcache/resultcache.go
+++ b/src/go/internal/agent/resultcache/resultcache.go
@@ -61,8 +61,9 @@ const (
type ResultCache interface {
Start()
Stop()
- UpdateOptions(options *agent.AgentOptions)
Upload(u Uploader)
+ // TODO: will be used once the runtime configuration reload is implemented
+ UpdateOptions(options *agent.AgentOptions)
}
type AgentData struct {
@@ -116,6 +117,7 @@ func (c *cacheData) Write(result *plugin.Result) {
c.input <- result
}
+// TODO: will be used once the runtime configuration reload is implemented
func (c *cacheData) UpdateOptions(options *agent.AgentOptions) {
c.input <- options
}
diff --git a/src/go/pkg/zbxlib/checks_linux.go b/src/go/pkg/zbxlib/checks_linux.go
index 74875f58d52..6443c5243d3 100644
--- a/src/go/pkg/zbxlib/checks_linux.go
+++ b/src/go/pkg/zbxlib/checks_linux.go
@@ -116,14 +116,6 @@ func resolveMetric(key string) (cfunc unsafe.Pointer) {
cfunc = unsafe.Pointer(C.VFS_DIR_COUNT)
case "vfs.dir.size":
cfunc = unsafe.Pointer(C.VFS_DIR_SIZE)
- case "vfs.fs.discovery":
- cfunc = unsafe.Pointer(C.VFS_FS_DISCOVERY)
- case "vfs.fs.inode":
- cfunc = unsafe.Pointer(C.VFS_FS_INODE)
- case "vfs.fs.size":
- cfunc = unsafe.Pointer(C.VFS_FS_SIZE)
- case "vfs.fs.get":
- cfunc = unsafe.Pointer(C.VFS_FS_GET)
case "vm.memory.size":
cfunc = unsafe.Pointer(C.VM_MEMORY_SIZE)
}
diff --git a/src/go/plugins/plugins_linux.go b/src/go/plugins/plugins_linux.go
index 714f7839e02..003250d6c66 100644
--- a/src/go/plugins/plugins_linux.go
+++ b/src/go/plugins/plugins_linux.go
@@ -38,6 +38,7 @@ import (
_ "zabbix.com/plugins/systemrun"
_ "zabbix.com/plugins/vfs/dev"
_ "zabbix.com/plugins/vfs/file"
+ _ "zabbix.com/plugins/vfs/fs"
_ "zabbix.com/plugins/web"
_ "zabbix.com/plugins/zabbix/async"
_ "zabbix.com/plugins/zabbix/stats"
diff --git a/src/go/plugins/vfs/fs/fs.go b/src/go/plugins/vfs/fs/fs.go
index 2b609e202ee..db9d23f85df 100644
--- a/src/go/plugins/vfs/fs/fs.go
+++ b/src/go/plugins/vfs/fs/fs.go
@@ -1,5 +1,3 @@
-// +build windows
-
/*
** Zabbix
** Copyright (C) 2001-2020 Zabbix SIA
@@ -53,6 +51,15 @@ type FsInfo struct {
FsType *string `json:"{#FSTYPE},omitempty"`
DriveType *string `json:"{#FSDRIVETYPE},omitempty"`
Bytes *FsStats `json:"bytes,omitempty"`
+ Inodes *FsStats `json:"inodes,omitempty"`
+}
+
+type FsInfoNew struct {
+ FsName *string `json:"fsname,omitempty"`
+ FsType *string `json:"fstype,omitempty"`
+ DriveType *string `json:"fsdrivetype,omitempty"`
+ Bytes *FsStats `json:"bytes,omitempty"`
+ Inodes *FsStats `json:"inodes,omitempty"`
}
type Plugin struct {
@@ -80,7 +87,7 @@ func (p *Plugin) exportGet(params []string) (value interface{}, err error) {
if len(params) != 0 {
return nil, errors.New(errorInvalidParameters)
}
- var d []*FsInfo
+ var d []*FsInfoNew
if d, err = p.getFsInfoStats(); err != nil {
return
}
@@ -91,7 +98,7 @@ func (p *Plugin) exportGet(params []string) (value interface{}, err error) {
return string(b), nil
}
-func (p *Plugin) exportSize(params []string) (value interface{}, err error) {
+func (p *Plugin) export(params []string, getStats func(string) (*FsStats, error)) (value interface{}, err error) {
if len(params) < 1 || params[0] == "" {
return nil, errors.New("Invalid first parameter.")
}
@@ -115,7 +122,7 @@ func (p *Plugin) exportSize(params []string) (value interface{}, err error) {
}
}
var stats *FsStats
- if stats, err = getFsStats(params[0]); err != nil {
+ if stats, err = getStats(params[0]); err != nil {
return
}
@@ -142,16 +149,10 @@ func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider)
case "vfs.fs.get":
return p.exportGet(params)
case "vfs.fs.size":
- return p.exportSize(params)
+ return p.export(params, getFsStats)
+ case "vfs.fs.inode":
+ return p.export(params, getFsInode)
default:
return nil, plugin.UnsupportedMetricError
}
}
-
-func init() {
- plugin.RegisterMetrics(&impl, "VfsFs",
- "vfs.fs.discovery", "List of mounted filesystems. Used for low-level discovery.",
- "vfs.fs.get", "List of mounted filesystems with statistics.",
- "vfs.fs.size", "Disk space in bytes or in percentage from total.",
- )
-}
diff --git a/src/go/plugins/vfs/fs/fs_nix.go b/src/go/plugins/vfs/fs/fs_nix.go
new file mode 100644
index 00000000000..99ea97f9fbf
--- /dev/null
+++ b/src/go/plugins/vfs/fs/fs_nix.go
@@ -0,0 +1,162 @@
+// +build !windows
+
+/*
+** Zabbix
+** Copyright (C) 2001-2019 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+package vfsfs
+
+import (
+ "bufio"
+ "io"
+ "os"
+ "strings"
+
+ "golang.org/x/sys/unix"
+ "zabbix.com/pkg/plugin"
+)
+
+func (p *Plugin) getFsInfoStats() (data []*FsInfoNew, err error) {
+ allData, err := p.getFsInfo()
+ if err != nil {
+ return nil, err
+ }
+
+ fsmap := make(map[string]*FsInfoNew)
+ for _, info := range allData {
+ bytes, err := getFsStats(*info.FsName)
+ if err != nil {
+ p.Debugf(`cannot discern stats for the mount: %s`, *info.FsName)
+ continue
+ }
+
+ inodes, err := getFsInode(*info.FsName)
+ if err != nil {
+ p.Debugf(`cannot discern inode for the mount: %s`, *info.FsName)
+ continue
+ }
+
+ if bytes.Total > 0 && inodes.Total > 0 {
+ fsmap[*info.FsName] = &FsInfoNew{info.FsName, info.FsType, nil, bytes, inodes}
+ }
+ }
+
+ allData, err = p.getFsInfo()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, info := range allData {
+ if fsInfo, ok := fsmap[*info.FsName]; ok {
+ data = append(data, fsInfo)
+ }
+ }
+
+ return
+}
+
+func (p *Plugin) readMounts(file io.Reader) (data []*FsInfo, err error) {
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ mnt := strings.Split(line, " ")
+ if len(mnt) < 3 {
+ p.Debugf(`cannot discern the mount in given line: %s`, line)
+ continue
+ }
+ data = append(data, &FsInfo{FsName: &mnt[1], FsType: &mnt[2]})
+ }
+
+ if err = scanner.Err(); err != nil {
+ return nil, err
+ }
+
+ return
+}
+
+func (p *Plugin) getFsInfo() (data []*FsInfo, err error) {
+ file, err := os.Open("/proc/mounts")
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ data, err = p.readMounts(file)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, nil
+}
+
+func getFsStats(path string) (stats *FsStats, err error) {
+ fs := unix.Statfs_t{}
+ err = unix.Statfs(path, &fs)
+ if err != nil {
+ return nil, err
+ }
+
+ var avialable uint64
+ if fs.Bavail > 0 {
+ avialable = fs.Bavail
+ }
+
+ total := fs.Blocks * uint64(fs.Bsize)
+ free := avialable * uint64(fs.Bsize)
+ used := (fs.Blocks - fs.Bfree) * uint64(fs.Bsize)
+ pfree := 100.00 * float64(avialable) / float64(fs.Blocks-fs.Bfree+fs.Bavail)
+ stats = &FsStats{
+ Total: total,
+ Free: free,
+ Used: used,
+ PFree: pfree,
+ PUsed: 100 - pfree,
+ }
+
+ return
+}
+
+func getFsInode(path string) (stats *FsStats, err error) {
+ fs := unix.Statfs_t{}
+ err = unix.Statfs(path, &fs)
+ if err != nil {
+ return nil, err
+ }
+
+ total := fs.Files
+ free := fs.Ffree
+ used := fs.Files - fs.Ffree
+ stats = &FsStats{
+ Total: total,
+ Free: free,
+ Used: used,
+ PFree: 100 * float64(free) / float64(total),
+ PUsed: 100 * float64(total-free) / float64(total),
+ }
+
+ return
+}
+
+func init() {
+ plugin.RegisterMetrics(&impl, "VfsFs",
+ "vfs.fs.discovery", "List of mounted filesystems. Used for low-level discovery.",
+ "vfs.fs.get", "List of mounted filesystems with statistics.",
+ "vfs.fs.size", "Disk space in bytes or in percentage from total.",
+ "vfs.fs.inode", "Disk space in bytes or in percentage from total.",
+ )
+}
diff --git a/src/go/plugins/vfs/fs/fs_windows.go b/src/go/plugins/vfs/fs/fs_windows.go
index 2cf2e1c4b96..05e41ecdedb 100644
--- a/src/go/plugins/vfs/fs/fs_windows.go
+++ b/src/go/plugins/vfs/fs/fs_windows.go
@@ -23,6 +23,7 @@ import (
"syscall"
"golang.org/x/sys/windows"
+ "zabbix.com/pkg/plugin"
)
func getMountPaths() (paths []string, err error) {
@@ -145,14 +146,14 @@ func (p *Plugin) getFsInfo() (data []*FsInfo, err error) {
return
}
-func (p *Plugin) getFsInfoStats() (data []*FsInfo, err error) {
+func (p *Plugin) getFsInfoStats() (data []*FsInfoNew, err error) {
var paths []string
if paths, err = getMountPaths(); err != nil {
return
}
- fsmap := make(map[string]*FsInfo)
+ fsmap := make(map[string]*FsInfoNew)
for _, path := range paths {
- var info FsInfo
+ var info FsInfoNew
if fsname, fstype, drivetype, fserr := getFsInfo(path); fserr == nil {
info.FsName = &fsname
info.FsType = &fstype
@@ -179,3 +180,15 @@ func (p *Plugin) getFsInfoStats() (data []*FsInfo, err error) {
}
return
}
+
+func getFsInode(string) (*FsStats, error) {
+ return nil, plugin.UnsupportedMetricError
+}
+
+func init() {
+ plugin.RegisterMetrics(&impl, "VfsFs",
+ "vfs.fs.discovery", "List of mounted filesystems. Used for low-level discovery.",
+ "vfs.fs.get", "List of mounted filesystems with statistics.",
+ "vfs.fs.size", "Disk space in bytes or in percentage from total.",
+ )
+}
diff --git a/src/go/plugins/zabbix/async/async_nix.go b/src/go/plugins/zabbix/async/async_nix.go
index 91c4155c424..031817c26ff 100644
--- a/src/go/plugins/zabbix/async/async_nix.go
+++ b/src/go/plugins/zabbix/async/async_nix.go
@@ -36,6 +36,5 @@ func getMetrics() []string {
"system.sw.os", "Operating system information.",
"system.swap.in", "Swap in (from device into memory) statistics.",
"system.swap.out", "Swap out (from memory onto device) statistics.",
- "vfs.fs.discovery", "List of mounted filesystems. Used for low-level discovery.",
}
}
diff --git a/src/go/plugins/zabbix/sync/sync_nix.go b/src/go/plugins/zabbix/sync/sync_nix.go
index 9a454bdadfb..1c9dee0c069 100644
--- a/src/go/plugins/zabbix/sync/sync_nix.go
+++ b/src/go/plugins/zabbix/sync/sync_nix.go
@@ -34,9 +34,6 @@ func getMetrics() []string {
"system.swap.size", "Swap space size in bytes or in percentage from total.",
"vfs.dir.count", "Directory entry count.",
"vfs.dir.size", "Directory size (in bytes).",
- "vfs.fs.inode", "Number or percentage of inodes.",
- "vfs.fs.size", "Disk space in bytes or in percentage from total.",
- "vfs.fs.get", "List of mounted filesystems with statistics.",
"vm.memory.size", "Memory size in bytes or in percentage from total.",
}
}
diff --git a/src/zabbix_server/housekeeper/housekeeper.c b/src/zabbix_server/housekeeper/housekeeper.c
index b8f98b41437..59861f47b7e 100644
--- a/src/zabbix_server/housekeeper/housekeeper.c
+++ b/src/zabbix_server/housekeeper/housekeeper.c
@@ -909,7 +909,7 @@ static int housekeeping_cleanup(void)
/* assemble list of tables included in the housekeeping procedure */
for (table = hk_cleanup_tables; NULL != table->name; table++)
{
- if (ZBX_HK_MODE_REGULAR != *table->poption_mode || ZBX_HK_OPTION_ENABLED == *table->poption_global)
+ if (ZBX_HK_MODE_REGULAR != *table->poption_mode)
continue;
table_name_esc = DBdyn_escape_string(table->name);
diff --git a/templates/db/oracle_odbc/README.md b/templates/db/oracle_odbc/README.md
index 59e1b0b456e..a49b3b317cd 100644
--- a/templates/db/oracle_odbc/README.md
+++ b/templates/db/oracle_odbc/README.md
@@ -15,7 +15,7 @@ This template was tested on:
> See [Zabbix template operation](https://www.zabbix.com/documentation/current/manual/config/templates_out_of_the_box/odbc_checks) for basic instructions.
-1. Create an Oracle DB user for monitoring.
+1. Create an Oracle DB user for monitoring:
```
CREATE USER zabbix_mon IDENTIFIED BY <PASSWORD>;
-- Grant access to the zabbix_mon user.
@@ -39,12 +39,12 @@ This template was tested on:
GRANT SELECT ON DBA_TABLESPACE_USAGE_METRICS TO zabbix_mon;
GRANT SELECT ON DBA_USERS TO zabbix_mon;
```
-**Note! Be sure that ODBC connects to Oracle with session parameter NLS_NUMERIC_CHARACTERS= '.,' It is important for correct display float numbers in Zabbix.**
+**Note! Make sure that ODBC connects to Oracle with session parameter NLS_NUMERIC_CHARACTERS= '.,' It is important for displaying float numbers in Zabbix correctly.**
2. Install the ODBC driver on the Zabbix server or the Zabbix proxy.
See Oracle documentation for instructions: https://www.oracle.com/database/technologies/releasenote-odbc-ic.html.
-3. Add new record to odbc.ini
+3. Add a new record to odbc.ini:
```
[$ORACLE.DSN]
Driver = Oracle 19 ODBC driver
@@ -53,17 +53,20 @@ DSN = $ORACLE.DSN
```
**Note! Credentials in the odbc.ini do not work for Oracle.**
-4. Check the connection via isql
+4. Check the connection via isql:
```
isql $TNS_NAME $DB_USER $DB_PASSWORD
```
-5. If result succesful (fix the mistakes in odbc.ini if not), configure zabbix-server for Oracle ENV Usage or zabbix-proxy if you use proxy for Oracle DB monitoring
+5. Сonfigure Zabbix server or Zabbix proxy for Oracle ENV Usage. Edit or add a new file:
+
Edit or add new file
/etc/sysconfig/zabbix-server # for server
+
/etc/sysconfig/zabbix-proxy # for proxy
- Add
+
+ Then, add:
```
export ORACLE_HOME=/usr/lib/oracle/19.6/client64
export PATH=$PATH:$ORACLE_HOME/bin
@@ -71,9 +74,9 @@ isql $TNS_NAME $DB_USER $DB_PASSWORD
export TNS_ADMIN=$ORACLE_HOME/network/admin
```
-6. Restart Zabbix-server or zabbix-proxy service
+6. Restart Zabbix server or Zabbix proxy.
-7. Set the user name and password in host macros ({$ORACLE.USER} and {$ORACLE.PASSWORD}).
+7. Set the username and password in host macros ({$ORACLE.USER} and {$ORACLE.PASSWORD}).
8. Set the {$ORACLE.DSN} in host macros.
@@ -92,24 +95,24 @@ No specific Zabbix configuration is required.
|{$ORACLE.ASM.USED.PCT.MAX.WARN} |<p>Maximum percentage of used ASM disk group for warning trigger expression.</p> |`90` |
|{$ORACLE.CONCURRENCY.MAX.WARN} |<p>Maximum percentage of sessions concurrency usage for trigger expression.</p> |`80` |
|{$ORACLE.DB.FILE.MAX.WARN} |<p>Maximum percentage of database files for trigger expression.</p> |`80` |
-|{$ORACLE.DBNAME.MATCHES} |<p>This macro is used in database discovery. It can be overridden on the host or linked template level.</p> |`.*` |
-|{$ORACLE.DBNAME.NOT_MATCHES} |<p>This macro is used in database discovery. It can be overridden on the host or linked template level.</p> |`PDB\$SEED` |
+|{$ORACLE.DBNAME.MATCHES} |<p>This macro is used in database discovery. It can be overridden on a host or linked template level.</p> |`.*` |
+|{$ORACLE.DBNAME.NOT_MATCHES} |<p>This macro is used in database discovery. It can be overridden on a host or linked template level.</p> |`PDB\$SEED` |
|{$ORACLE.DSN} |<p>System data source name</p> |`<Put your DSN here>` |
-|{$ORACLE.EXPIRE.PASSWORD.MIN.WARN} |<p>The number of days before password expired for trigger expression.</p> |`7` |
+|{$ORACLE.EXPIRE.PASSWORD.MIN.WARN} |<p>Number of days of warning before password expires (for trigger expression).</p> |`7` |
|{$ORACLE.PASSWORD} |<p>Oracle user password.</p> |`<Put your password here>` |
-|{$ORACLE.PGA.USE.MAX.WARN} |<p>Maximum percentage of PGA usage for trigger expression.</p> |`90` |
+|{$ORACLE.PGA.USE.MAX.WARN} |<p>Maximum percentage of PGA usage alert treshold (for trigger expression).</p> |`90` |
|{$ORACLE.PORT} |<p>Oracle DB TCP port.</p> |`1521` |
-|{$ORACLE.PROCESSES.MAX.WARN} |<p>Maximum percentage of active processes for trigger expression.</p> |`80` |
-|{$ORACLE.REDO.MIN.WARN} |<p>Minimum number of REDO logs for trigger expression.</p> |`3` |
-|{$ORACLE.SESSION.LOCK.MAX.TIME} |<p>Maximum wait seconds for count of long time locked sessions query.</p> |`600` |
-|{$ORACLE.SESSION.LONG.LOCK.MAX.WARN} |<p>Maximum number of long locked sessions usage for trigger expression.</p> |`3` |
-|{$ORACLE.SESSIONS.LOCK.MAX.WARN} |<p>Maximum percentage of locked sessions usage for trigger expression.</p> |`20` |
-|{$ORACLE.SESSIONS.MAX.WARN} |<p>Maximum percentage of active sessions for trigger expression.</p> |`80` |
-|{$ORACLE.SHARED.FREE.MIN.WARN} |<p>Minimum percent of free shared pool for trigger expression.</p> |`5` |
-|{$ORACLE.TABLESPACE.NAME.MATCHES} |<p>This macro is used in tablespace discovery. It can be overridden on the host or linked template level.</p> |`.*` |
-|{$ORACLE.TABLESPACE.NAME.NOT_MATCHES} |<p>This macro is used in tablespace discovery. It can be overridden on the host or linked template level.</p> |`CHANGE_IF_NEEDED` |
-|{$ORACLE.TBS.USED.PCT.MAX.HIGH} |<p>Maximum percentage of used tablespace for high trigger expression.</p> |`90` |
-|{$ORACLE.TBS.USED.PCT.MAX.WARN} |<p>Maximum percentage of used tablespace for warning trigger expression.</p> |`80` |
+|{$ORACLE.PROCESSES.MAX.WARN} |<p>Maximum percentage of active processes alert treshold (for trigger expression).</p> |`80` |
+|{$ORACLE.REDO.MIN.WARN} |<p>Minimum number of REDO logs alert treshold (for trigger expression).</p> |`3` |
+|{$ORACLE.SESSION.LOCK.MAX.TIME} |<p>Maximum session lock duration in seconds for count the session as a prolongely locked query.</p> |`600` |
+|{$ORACLE.SESSION.LONG.LOCK.MAX.WARN} |<p>Maximum number of the prolongely locked sessions alert treshold (for trigger expression).</p> |`3` |
+|{$ORACLE.SESSIONS.LOCK.MAX.WARN} |<p>Maximum percentage of locked sessions alert treshold (for trigger expression).</p> |`20` |
+|{$ORACLE.SESSIONS.MAX.WARN} |<p>Maximum percentage of active sessions alert treshold (for trigger expression).</p> |`80` |
+|{$ORACLE.SHARED.FREE.MIN.WARN} |<p>Minimum percentage of free shared pool alert treshold (for trigger expression).</p> |`5` |
+|{$ORACLE.TABLESPACE.NAME.MATCHES} |<p>This macro is used in tablespace discovery. It can be overridden on a host or linked template level.</p> |`.*` |
+|{$ORACLE.TABLESPACE.NAME.NOT_MATCHES} |<p>This macro is used in tablespace discovery. It can be overridden on a host or linked template level.</p> |`CHANGE_IF_NEEDED` |
+|{$ORACLE.TBS.USED.PCT.MAX.HIGH} |<p>Maximum percentage of used tablespace high severity alert treshold (for trigger expression).</p> |`90` |
+|{$ORACLE.TBS.USED.PCT.MAX.WARN} |<p>Maximum percentage of used tablespace warning severity alert treshold (for trigger expression).</p> |`80` |
|{$ORACLE.USER} |<p>Oracle username.</p> |`<Put your username here>` |
## Template links
@@ -163,7 +166,7 @@ There are no template links in this template.
|Oracle |Oracle: Active background sessions |<p>The number of active background sessions.</p> |DEPENDENT |oracle.session_active_background<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Active Background')].VALUE.first()`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> 0`</p> |
|Oracle |Oracle: Inactive user sessions |<p>The number of inactive user sessions.</p> |DEPENDENT |oracle.session_inactive_user<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Inactive User')].VALUE.first()`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> 0`</p> |
|Oracle |Oracle: Sessions lock rate |<p>The percentage of locked sessions. Locks are mechanisms that prevent destructive interaction between transactions accessing the same resource—either user objects such as tables and rows or system objects not visible to users, such as shared data structures in memory and data dictionary rows.</p> |DEPENDENT |oracle.session_lock_rate<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Lock rate')].VALUE.first()`</p> |
-|Oracle |Oracle: Sessions locked over {$ORACLE.SESSION.LOCK.MAX.TIME}s |<p>Count of long time locked sessions. (You can change maximum session lock duration in seconds for query by {$ORACLE.SESSION.LOCK.MAX.TIME} macro. Default 600 sec)</p> |DEPENDENT |oracle.session_long_time_locked<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Long time locked')].VALUE.first()`</p> |
+|Oracle |Oracle: Sessions locked over {$ORACLE.SESSION.LOCK.MAX.TIME}s |<p>Count of the prolongely locked sessions. (You can change maximum session lock duration in seconds for query by {$ORACLE.SESSION.LOCK.MAX.TIME} macro. Default 600 sec)</p> |DEPENDENT |oracle.session_long_time_locked<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Long time locked')].VALUE.first()`</p> |
|Oracle |Oracle: Sessions concurrency |<p>The percentage of concurrency. Concurrency is a DB behaviour when different transactions request to change the same resource - in case of modifying data transactions sequentially block temporarily the right to change data, the rest of the transactions are waiting for access. In the case when access for resource is locked for a long time, then the concurrency grows (like the transaction queue) and this often has an extremely negative impact on performance. A high contention value does not indicate the root cause of the problem, but is a signal to search for it.</p> |DEPENDENT |oracle.session_concurrency_rate<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SESSION::Concurrency rate')].VALUE.first()`</p> |
|Oracle |Oracle: User '{$ORACLE.USER}' expire password |<p>The number of days before zabbix account password expired.</p> |DEPENDENT |oracle.user_expire_password<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='USER::Expire password')].VALUE.first()`</p> |
|Oracle |Oracle: Active serial sessions |<p>The number of active serial sessions.</p> |DEPENDENT |oracle.active_serial_sessions<p>**Preprocessing**:</p><p>- JSONPATH: `$[?(@.METRIC=='SYS::Active Serial Sessions')].VALUE.first()`</p> |
@@ -228,32 +231,32 @@ There are no template links in this template.
|Oracle: LISTENER process is not running | |`{TEMPLATE_NAME:proc.num[,,,"tnslsnr LISTENER"].max(#3)}=0` |DISASTER | |
|Oracle: Version has changed (new version value received: {ITEM.VALUE}) |<p>Oracle DB version has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.version.diff()}=1 and {TEMPLATE_NAME:oracle.version.strlen()}>0` |INFO |<p>Manual close: YES</p> |
|Oracle: has been restarted (uptime < 10m) |<p>Uptime is less than 10 minutes</p> |`{TEMPLATE_NAME:oracle.uptime.last()}<10m` |INFO |<p>Manual close: YES</p> |
-|Oracle: Failed to fetch info data (or no data for 5m) |<p>Zabbix has not received data for items for the last 5 minutes. The database might be unavailable for connecting</p> |`{TEMPLATE_NAME:oracle.uptime.nodata(5m)}=1` |WARNING |<p>**Depends on**:</p><p>- Oracle: Port {$ORACLE.PORT} is unavailable</p> |
+|Oracle: Failed to fetch info data (or no data for 5m) |<p>Zabbix has not received data for items for the last 5 minutes. The database might be unavailable for connecting.</p> |`{TEMPLATE_NAME:oracle.uptime.nodata(5m)}=1` |WARNING |<p>**Depends on**:</p><p>- Oracle: Port {$ORACLE.PORT} is unavailable</p> |
|Oracle: Instance name has changed (new name received: {ITEM.VALUE}) |<p>Oracle DB Instance name has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.instance_name.diff()}=1 and {TEMPLATE_NAME:oracle.instance_name.strlen()}>0` |INFO |<p>Manual close: YES</p> |
|Oracle: Instance hostname has changed (new hostname received: {ITEM.VALUE}) |<p>Oracle DB Instance hostname has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.instance_hostname.diff()}=1 and {TEMPLATE_NAME:oracle.instance_hostname.strlen()}>0` |INFO |<p>Manual close: YES</p> |
-|Oracle: Too many active processes (over {$ORACLE.PROCESSES.MAX.WARN}% for 5 min) |<p>Active process are using more than {$ORACLE.PROCESSES.MAX.WARN}% of the available Process number</p> |`{TEMPLATE_NAME:oracle.processes_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.processes_limit.last()} > {$ORACLE.PROCESSES.MAX.WARN}` |WARNING | |
-|Oracle: Too many database files (over {$ORACLE.DB.FILE.MAX.WARN}% for 5 min) |<p>Number of datafiles more than {$ORACLE.DB.FILE.MAX.WARN}% of the available datafile files limit.</p> |`{TEMPLATE_NAME:oracle.db_files_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.db_files_limit.last()} > {$ORACLE.DB.FILE.MAX.WARN}` |WARNING | |
-|Oracle: Shared pool free is too low (less {$ORACLE.SHARED.FREE.MIN.WARN}% for 5m) |<p>The shared pool free memory percent less than {$ORACLE.SHARED.FREE.MIN.WARN}% in the last 5 minutes.</p> |`{TEMPLATE_NAME:oracle.shared_pool_free.max(5m)}<{$ORACLE.SHARED.FREE.MIN.WARN}` |WARNING | |
-|Oracle: Too many active sessions (over {$ORACLE.SESSIONS.MAX.WARN}% for 5 min) |<p>Active sessions are using more than {$ORACLE.SESSIONS.MAX.WARN}% of the available sessions</p> |`{TEMPLATE_NAME:oracle.session_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.session_limit.last()} > {$ORACLE.SESSIONS.MAX.WARN}` |WARNING | |
+|Oracle: Too many active processes (over {$ORACLE.PROCESSES.MAX.WARN}% for 5 min) |<p>Active processes are using more than {$ORACLE.PROCESSES.MAX.WARN}% of the available number of processes.</p> |`{TEMPLATE_NAME:oracle.processes_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.processes_limit.last()} > {$ORACLE.PROCESSES.MAX.WARN}` |WARNING | |
+|Oracle: Too many database files (over {$ORACLE.DB.FILE.MAX.WARN}% for 5 min) |<p>Number of datafiles is higher than {$ORACLE.DB.FILE.MAX.WARN}% of the available datafile files limit.</p> |`{TEMPLATE_NAME:oracle.db_files_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.db_files_limit.last()} > {$ORACLE.DB.FILE.MAX.WARN}` |WARNING | |
+|Oracle: Shared pool free is too low (less {$ORACLE.SHARED.FREE.MIN.WARN}% for 5m) |<p>The shared pool free memory percent has been less than {$ORACLE.SHARED.FREE.MIN.WARN}% in the last 5 minutes.</p> |`{TEMPLATE_NAME:oracle.shared_pool_free.max(5m)}<{$ORACLE.SHARED.FREE.MIN.WARN}` |WARNING | |
+|Oracle: Too many active sessions (over {$ORACLE.SESSIONS.MAX.WARN}% for 5 min) |<p>Active sessions are using more than {$ORACLE.SESSIONS.MAX.WARN}% of the available sessions.</p> |`{TEMPLATE_NAME:oracle.session_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.session_limit.last()} > {$ORACLE.SESSIONS.MAX.WARN}` |WARNING | |
|Oracle: Too many locked sessions (over {$ORACLE.SESSIONS.LOCK.MAX.WARN}% for 5 min) |<p>Number of locked sessions is over {$ORACLE.SESSIONS.LOCK.MAX.WARN}% of the running sessions.</p> |`{TEMPLATE_NAME:oracle.session_lock_rate.min(5m)} > {$ORACLE.SESSIONS.LOCK.MAX.WARN}` |WARNING | |
|Oracle: Too many sessions locked over {$ORACLE.SESSION.LOCK.MAX.TIME}s (over {$ORACLE.SESSION.LONG.LOCK.MAX.WARN} for 5 min) |<p>Number of sessions locked over {$ORACLE.SESSION.LOCK.MAX.TIME} seconds is too high. Long-term locks can negatively affect database performance, therefore, if they are detected, you should first find the most difficult queries from the database point of view and analyze possible resource leaks.</p> |`{TEMPLATE_NAME:oracle.session_long_time_locked.min(5m)} > {$ORACLE.SESSION.LONG.LOCK.MAX.WARN}` |WARNING | |
|Oracle: Too hight database concurrency (over {$ORACLE.CONCURRENCY.MAX.WARN}% for 5 min) |<p>Concurrency rate is over {$ORACLE.CONCURRENCY.MAX.WARN}%. A high contention value does not indicate the root cause of the problem, but is a signal to search for it. In the case of high competition, an analysis of resource consumption should be carried out, the most "heavy" queries made in the database, possibly - session tracing. All this will help determine the root cause and possible optimization points both in the database configuration and in the logic of building queries of the application itself.</p> |`{TEMPLATE_NAME:oracle.session_concurrency_rate.min(5m)} > {$ORACLE.CONCURRENCY.MAX.WARN}` |WARNING | |
-|Oracle: Zabbix account will expire soon (under {$ORACLE.EXPIRE.PASSWORD.MIN.WARN} days) |<p>Password for zabbix user in database will expire soon.</p> |`{TEMPLATE_NAME:oracle.user_expire_password.last()} < {$ORACLE.EXPIRE.PASSWORD.MIN.WARN}` |WARNING | |
-|Oracle: Total PGA inuse is too high (over {$ORACLE.PGA.USE.MAX.WARN}% for 5 min) |<p>Total PGA inuse is more than {$ORACLE.PGA.USE.MAX.WARN}% of PGA_AGGREGATE_TARGET.</p> |`{TEMPLATE_NAME:oracle.total_pga_used.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.pga_target.last()} > {$ORACLE.PGA.USE.MAX.WARN}` |WARNING | |
+|Oracle: Zabbix account will expire soon (under {$ORACLE.EXPIRE.PASSWORD.MIN.WARN} days) |<p>Password for zabbix user in the database will expire soon.</p> |`{TEMPLATE_NAME:oracle.user_expire_password.last()} < {$ORACLE.EXPIRE.PASSWORD.MIN.WARN}` |WARNING | |
+|Oracle: Total PGA inuse is too high (over {$ORACLE.PGA.USE.MAX.WARN}% for 5 min) |<p>Total PGA in use is more than {$ORACLE.PGA.USE.MAX.WARN}% of PGA_AGGREGATE_TARGET.</p> |`{TEMPLATE_NAME:oracle.total_pga_used.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.pga_target.last()} > {$ORACLE.PGA.USE.MAX.WARN}` |WARNING | |
|Oracle: Number of REDO logs available for switching is too low (less {$ORACLE.REDO.MIN.WARN} for 5 min) |<p>Number of available for log switching inactive/unused REDOs is low (Database down risk)</p> |`{TEMPLATE_NAME:oracle.redo_logs_available.max(5m)} < {$ORACLE.REDO.MIN.WARN}` |WARNING | |
|Oracle Database '{#DBNAME}': Open status in mount mode |<p>The Oracle DB has a MOUNTED state.</p> |`{TEMPLATE_NAME:oracle.db_open_mode["{#DBNAME}"].last()}=1` |WARNING | |
|Oracle Database '{#DBNAME}': Open status has changed (new value received: {ITEM.VALUE}) |<p>Oracle DB open status has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.db_open_mode["{#DBNAME}"].diff()}=1` |INFO |<p>Manual close: YES</p><p>**Depends on**:</p><p>- Oracle Database '{#DBNAME}': Open status in mount mode</p> |
|Oracle Database '{#DBNAME}': Role has changed (new value received: {ITEM.VALUE}) |<p>Oracle DB role has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.db_role["{#DBNAME}"].diff()}=1` |INFO |<p>Manual close: YES</p> |
-|Oracle Database '{#DBNAME}': Force logging is deactivate for DB with active Archivelog |<p>Force Logging mode - it is very important metric for Databases in 'ARCHIVELOG'. This feature allows to forcibly write all transactions to the REDO.</p> |`{TEMPLATE_NAME:oracle.db_force_logging["{#DBNAME}"].last()} = 0 and {Template DB Oracle by ODBC:oracle.db_log_mode["{#DBNAME}"].last()} = 1` |WARNING | |
+|Oracle Database '{#DBNAME}': Force logging is deactivated for DB with active Archivelog |<p>Force Logging mode - it is very important metric for Databases in 'ARCHIVELOG'. This feature allows to forcibly write all transactions to the REDO.</p> |`{TEMPLATE_NAME:oracle.db_force_logging["{#DBNAME}"].last()} = 0 and {Template DB Oracle by ODBC:oracle.db_log_mode["{#DBNAME}"].last()} = 1` |WARNING | |
|Oracle Database '{#DBNAME}': Open status in mount mode |<p>The Oracle DB has a MOUNTED state.</p> |`{TEMPLATE_NAME:oracle.pdb_open_mode["{#DBNAME}"].last()}=1` |WARNING | |
|Oracle Database '{#DBNAME}': Open status has changed (new value received: {ITEM.VALUE}) |<p>Oracle DB open status has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.pdb_open_mode["{#DBNAME}"].diff()}=1` |INFO |<p>Manual close: YES</p> |
-|Oracle TBS '{#TABLESPACE}': Tablespace usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.WARN}% for 5m) | |`{TEMPLATE_NAME:oracle.tbs_used_pct["{#TABLESPACE}"].min(5m)}>{$ORACLE.TBS.USED.PCT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- Oracle TBS '{#TABLESPACE}': Tablespace Usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m)</p> |
-|Oracle TBS '{#TABLESPACE}': Tablespace Usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m) | |`{TEMPLATE_NAME:oracle.tbs_used_pct["{#TABLESPACE}"].min(5m)}>{$ORACLE.TBS.USED.PCT.MAX.HIGH}` |HIGH | |
-|Oracle TBS '{#TABLESPACE}': Tablespase is OFFLINE |<p>The tablespase is in offline state.</p> |`{TEMPLATE_NAME:oracle.tbs_status["{#TABLESPACE}"].last()}=2` |WARNING | |
+|Oracle TBS '{#TABLESPACE}': Tablespace usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.WARN}% for 5m). | |`{TEMPLATE_NAME:oracle.tbs_used_pct["{#TABLESPACE}"].min(5m)}>{$ORACLE.TBS.USED.PCT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- Oracle TBS '{#TABLESPACE}': Tablespace Usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m).</p> |
+|Oracle TBS '{#TABLESPACE}': Tablespace Usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m). | |`{TEMPLATE_NAME:oracle.tbs_used_pct["{#TABLESPACE}"].min(5m)}>{$ORACLE.TBS.USED.PCT.MAX.HIGH}` |HIGH | |
+|Oracle TBS '{#TABLESPACE}': Tablespase is OFFLINE |<p>The tablespase is in the offline state.</p> |`{TEMPLATE_NAME:oracle.tbs_status["{#TABLESPACE}"].last()}=2` |WARNING | |
|Oracle TBS '{#TABLESPACE}': Tablespace status has changed (new value received: {ITEM.VALUE}) |<p>Oracle tablespace status has changed. Ack to close.</p> |`{TEMPLATE_NAME:oracle.tbs_status["{#TABLESPACE}"].diff()}=1` |INFO |<p>Manual close: YES</p><p>**Depends on**:</p><p>- Oracle TBS '{#TABLESPACE}': Tablespase is OFFLINE</p> |
|Archivelog '{#DEST_NAME}': Log Archive is not valid |<p>ARL destination not in 3 - Valid or 2 - Deferred.</p> |`{TEMPLATE_NAME:oracle.archivelog_log_status["{#DEST_NAME}"].last()}<2` |HIGH | |
-|ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.WARN}% for 5m) | |`{TEMPLATE_NAME:oracle.asm_used_pct["{#DG_NAME}"].min(5m)}>{$ORACLE.ASM.USED.PCT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</p> |
-|ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m) | |`{TEMPLATE_NAME:oracle.asm_used_pct["{#DG_NAME}"].min(5m)}>{$ORACLE.ASM.USED.PCT.MAX.HIGH}` |HIGH | |
+|ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.WARN}% for 5m) |<p>Usage percent of ASM disk group is over {$ORACLE.ASM.USED.PCT.MAX.WARN}</p> |`{TEMPLATE_NAME:oracle.asm_used_pct["{#DG_NAME}"].min(5m)}>{$ORACLE.ASM.USED.PCT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</p> |
+|ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m) |<p>Usage percent of ASM disk group is over {$ORACLE.ASM.USED.PCT.MAX.WARN}</p> |`{TEMPLATE_NAME:oracle.asm_used_pct["{#DG_NAME}"].min(5m)}>{$ORACLE.ASM.USED.PCT.MAX.HIGH}` |HIGH | |
## Feedback
diff --git a/templates/db/oracle_odbc/template_db_oracle_odbc.xml b/templates/db/oracle_odbc/template_db_oracle_odbc.xml
index ee4527503b6..fe21fa52af5 100644
--- a/templates/db/oracle_odbc/template_db_oracle_odbc.xml
+++ b/templates/db/oracle_odbc/template_db_oracle_odbc.xml
@@ -1485,7 +1485,7 @@ SELECT
<delay>0</delay>
<history>7d</history>
<value_type>FLOAT</value_type>
- <description>Count of long time locked sessions. (You can change maximum session lock duration in seconds for query by {$ORACLE.SESSION.LOCK.MAX.TIME} macro. Default 600 sec)</description>
+ <description>Count of the prolongely locked sessions. (You can change maximum session lock duration in seconds for query by {$ORACLE.SESSION.LOCK.MAX.TIME} macro. Default 600 sec)</description>
<applications>
<application>
<name>Oracle</name>
@@ -1687,7 +1687,7 @@ SELECT
<expression>{max(5m)}&lt;{$ORACLE.SHARED.FREE.MIN.WARN}</expression>
<name>Oracle: Shared pool free is too low (less {$ORACLE.SHARED.FREE.MIN.WARN}% for 5m)</name>
<priority>WARNING</priority>
- <description>The shared pool free memory percent less than {$ORACLE.SHARED.FREE.MIN.WARN}% in the last 5 minutes.</description>
+ <description>The shared pool free memory percent has been less than {$ORACLE.SHARED.FREE.MIN.WARN}% in the last 5 minutes.</description>
</trigger>
</triggers>
</item>
@@ -1842,7 +1842,7 @@ SELECT
<expression>{nodata(5m)}=1</expression>
<name>Oracle: Failed to fetch info data (or no data for 5m)</name>
<priority>WARNING</priority>
- <description>Zabbix has not received data for items for the last 5 minutes. The database might be unavailable for connecting</description>
+ <description>Zabbix has not received data for items for the last 5 minutes. The database might be unavailable for connecting.</description>
<dependencies>
<dependency>
<name>Oracle: Port {$ORACLE.PORT} is unavailable</name>
@@ -1880,7 +1880,7 @@ SELECT
<expression>{last()} &lt; {$ORACLE.EXPIRE.PASSWORD.MIN.WARN}</expression>
<name>Oracle: Zabbix account will expire soon (under {$ORACLE.EXPIRE.PASSWORD.MIN.WARN} days)</name>
<priority>WARNING</priority>
- <description>Password for zabbix user in database will expire soon.</description>
+ <description>Password for zabbix user in the database will expire soon.</description>
</trigger>
</triggers>
</item>
@@ -2147,19 +2147,21 @@ FROM v$archive_dest d , v$database db WHERE d.status != 'INACTIVE' AND db.log_mo
<trigger_prototypes>
<trigger_prototype>
<expression>{min(5m)}&gt;{$ORACLE.ASM.USED.PCT.MAX.WARN}</expression>
- <name>ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.WARN}% for 5m)</name>
+ <name>ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.WARN}% for 5m)</name>
<priority>WARNING</priority>
+ <description>Usage percent of ASM disk group is over {$ORACLE.ASM.USED.PCT.MAX.WARN}</description>
<dependencies>
<dependency>
- <name>ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</name>
+ <name>ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</name>
<expression>{Template DB Oracle by ODBC:oracle.asm_used_pct["{#DG_NAME}"].min(5m)}&gt;{$ORACLE.ASM.USED.PCT.MAX.HIGH}</expression>
</dependency>
</dependencies>
</trigger_prototype>
<trigger_prototype>
<expression>{min(5m)}&gt;{$ORACLE.ASM.USED.PCT.MAX.HIGH}</expression>
- <name>ASM '{#DG_NAME}': Disk group usage is is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</name>
+ <name>ASM '{#DG_NAME}': Disk group usage is too high (over {$ORACLE.ASM.USED.PCT.MAX.HIGH}% for 5m)</name>
<priority>HIGH</priority>
+ <description>Usage percent of ASM disk group is over {$ORACLE.ASM.USED.PCT.MAX.WARN}</description>
</trigger_prototype>
</trigger_prototypes>
</item_prototype>
@@ -2365,9 +2367,9 @@ FROM v$archive_dest d , v$database db WHERE d.status != 'INACTIVE' AND db.log_mo
<trigger_prototypes>
<trigger_prototype>
<expression>{Template DB Oracle by ODBC:oracle.db_force_logging["{#DBNAME}"].last()} = 0 and {Template DB Oracle by ODBC:oracle.db_log_mode["{#DBNAME}"].last()} = 1</expression>
- <name>Oracle Database '{#DBNAME}': Force logging is deactivate for DB with active Archivelog</name>
+ <name>Oracle Database '{#DBNAME}': Force logging is deactivated for DB with active Archivelog</name>
<priority>WARNING</priority>
- <description>Force Logging mode - it is very important metric for Databases in 'ARCHIVELOG'. This feature allows to forcibly write all transactions to the REDO.</description>
+ <description>Force Logging mode - it is very important metric for Databases in 'ARCHIVELOG'. This feature allows to forcibly write all transactions to the REDO.</description>
</trigger_prototype>
</trigger_prototypes>
</discovery_rule>
@@ -2545,7 +2547,7 @@ contents FROM DBA_TABLESPACES;</params>
<expression>{last()}=2</expression>
<name>Oracle TBS '{#TABLESPACE}': Tablespase is OFFLINE</name>
<priority>WARNING</priority>
- <description>The tablespase is in offline state.</description>
+ <description>The tablespase is in the offline state.</description>
</trigger_prototype>
<trigger_prototype>
<expression>{diff()}=1</expression>
@@ -2609,18 +2611,18 @@ contents FROM DBA_TABLESPACES;</params>
<trigger_prototypes>
<trigger_prototype>
<expression>{min(5m)}&gt;{$ORACLE.TBS.USED.PCT.MAX.WARN}</expression>
- <name>Oracle TBS '{#TABLESPACE}': Tablespace usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.WARN}% for 5m)</name>
+ <name>Oracle TBS '{#TABLESPACE}': Tablespace usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.WARN}% for 5m).</name>
<priority>WARNING</priority>
<dependencies>
<dependency>
- <name>Oracle TBS '{#TABLESPACE}': Tablespace Usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m)</name>
+ <name>Oracle TBS '{#TABLESPACE}': Tablespace Usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m).</name>
<expression>{Template DB Oracle by ODBC:oracle.tbs_used_pct["{#TABLESPACE}"].min(5m)}&gt;{$ORACLE.TBS.USED.PCT.MAX.HIGH}</expression>
</dependency>
</dependencies>
</trigger_prototype>
<trigger_prototype>
<expression>{min(5m)}&gt;{$ORACLE.TBS.USED.PCT.MAX.HIGH}</expression>
- <name>Oracle TBS '{#TABLESPACE}': Tablespace Usage is is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m)</name>
+ <name>Oracle TBS '{#TABLESPACE}': Tablespace Usage is too high (over {$ORACLE.TBS.USED.PCT.MAX.HIGH}% for 5m).</name>
<priority>HIGH</priority>
</trigger_prototype>
</trigger_prototypes>
@@ -2675,12 +2677,12 @@ contents FROM DBA_TABLESPACES;</params>
<macro>
<macro>{$ORACLE.DBNAME.MATCHES}</macro>
<value>.*</value>
- <description>This macro is used in database discovery. It can be overridden on the host or linked template level.</description>
+ <description>This macro is used in database discovery. It can be overridden on a host or linked template level.</description>
</macro>
<macro>
<macro>{$ORACLE.DBNAME.NOT_MATCHES}</macro>
<value>PDB\$SEED</value>
- <description>This macro is used in database discovery. It can be overridden on the host or linked template level.</description>
+ <description>This macro is used in database discovery. It can be overridden on a host or linked template level.</description>
</macro>
<macro>
<macro>{$ORACLE.DSN}</macro>
@@ -2690,7 +2692,7 @@ contents FROM DBA_TABLESPACES;</params>
<macro>
<macro>{$ORACLE.EXPIRE.PASSWORD.MIN.WARN}</macro>
<value>7</value>
- <description>The number of days before password expired for trigger expression.</description>
+ <description>Number of days of warning before password expires (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.PASSWORD}</macro>
@@ -2700,7 +2702,7 @@ contents FROM DBA_TABLESPACES;</params>
<macro>
<macro>{$ORACLE.PGA.USE.MAX.WARN}</macro>
<value>90</value>
- <description>Maximum percentage of PGA usage for trigger expression.</description>
+ <description>Maximum percentage of PGA usage alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.PORT}</macro>
@@ -2710,57 +2712,57 @@ contents FROM DBA_TABLESPACES;</params>
<macro>
<macro>{$ORACLE.PROCESSES.MAX.WARN}</macro>
<value>80</value>
- <description>Maximum percentage of active processes for trigger expression.</description>
+ <description>Maximum percentage of active processes alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.REDO.MIN.WARN}</macro>
<value>3</value>
- <description>Minimum number of REDO logs for trigger expression.</description>
+ <description>Minimum number of REDO logs alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.SESSION.LOCK.MAX.TIME}</macro>
<value>600</value>
- <description>Maximum wait seconds for count of long time locked sessions query.</description>
+ <description>Maximum session lock duration in seconds for count the session as a prolongely locked query.</description>
</macro>
<macro>
<macro>{$ORACLE.SESSION.LONG.LOCK.MAX.WARN}</macro>
<value>3</value>
- <description>Maximum number of long locked sessions usage for trigger expression.</description>
+ <description>Maximum number of the prolongely locked sessions alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.SESSIONS.LOCK.MAX.WARN}</macro>
<value>20</value>
- <description>Maximum percentage of locked sessions usage for trigger expression.</description>
+ <description>Maximum percentage of locked sessions alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.SESSIONS.MAX.WARN}</macro>
<value>80</value>
- <description>Maximum percentage of active sessions for trigger expression.</description>
+ <description>Maximum percentage of active sessions alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.SHARED.FREE.MIN.WARN}</macro>
<value>5</value>
- <description>Minimum percent of free shared pool for trigger expression.</description>
+ <description>Minimum percentage of free shared pool alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.TABLESPACE.NAME.MATCHES}</macro>
<value>.*</value>
- <description>This macro is used in tablespace discovery. It can be overridden on the host or linked template level.</description>
+ <description>This macro is used in tablespace discovery. It can be overridden on a host or linked template level.</description>
</macro>
<macro>
<macro>{$ORACLE.TABLESPACE.NAME.NOT_MATCHES}</macro>
<value>CHANGE_IF_NEEDED</value>
- <description>This macro is used in tablespace discovery. It can be overridden on the host or linked template level.</description>
+ <description>This macro is used in tablespace discovery. It can be overridden on a host or linked template level.</description>
</macro>
<macro>
<macro>{$ORACLE.TBS.USED.PCT.MAX.HIGH}</macro>
<value>90</value>
- <description>Maximum percentage of used tablespace for high trigger expression.</description>
+ <description>Maximum percentage of used tablespace high severity alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.TBS.USED.PCT.MAX.WARN}</macro>
<value>80</value>
- <description>Maximum percentage of used tablespace for warning trigger expression.</description>
+ <description>Maximum percentage of used tablespace warning severity alert treshold (for trigger expression).</description>
</macro>
<macro>
<macro>{$ORACLE.USER}</macro>
@@ -2781,25 +2783,25 @@ contents FROM DBA_TABLESPACES;</params>
<expression>{Template DB Oracle by ODBC:oracle.processes_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.processes_limit.last()} &gt; {$ORACLE.PROCESSES.MAX.WARN}</expression>
<name>Oracle: Too many active processes (over {$ORACLE.PROCESSES.MAX.WARN}% for 5 min)</name>
<priority>WARNING</priority>
- <description>Active process are using more than {$ORACLE.PROCESSES.MAX.WARN}% of the available Process number</description>
+ <description>Active processes are using more than {$ORACLE.PROCESSES.MAX.WARN}% of the available number of processes.</description>
</trigger>
<trigger>
<expression>{Template DB Oracle by ODBC:oracle.session_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.session_limit.last()} &gt; {$ORACLE.SESSIONS.MAX.WARN}</expression>
<name>Oracle: Too many active sessions (over {$ORACLE.SESSIONS.MAX.WARN}% for 5 min)</name>
<priority>WARNING</priority>
- <description>Active sessions are using more than {$ORACLE.SESSIONS.MAX.WARN}% of the available sessions</description>
+ <description>Active sessions are using more than {$ORACLE.SESSIONS.MAX.WARN}% of the available sessions.</description>
</trigger>
<trigger>
<expression>{Template DB Oracle by ODBC:oracle.db_files_count.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.db_files_limit.last()} &gt; {$ORACLE.DB.FILE.MAX.WARN}</expression>
<name>Oracle: Too many database files (over {$ORACLE.DB.FILE.MAX.WARN}% for 5 min)</name>
<priority>WARNING</priority>
- <description>Number of datafiles more than {$ORACLE.DB.FILE.MAX.WARN}% of the available datafile files limit.</description>
+ <description>Number of datafiles is higher than {$ORACLE.DB.FILE.MAX.WARN}% of the available datafile files limit.</description>
</trigger>
<trigger>
<expression>{Template DB Oracle by ODBC:oracle.total_pga_used.min(5m)} * 100 / {Template DB Oracle by ODBC:oracle.pga_target.last()} &gt; {$ORACLE.PGA.USE.MAX.WARN}</expression>
<name>Oracle: Total PGA inuse is too high (over {$ORACLE.PGA.USE.MAX.WARN}% for 5 min)</name>
<priority>WARNING</priority>
- <description>Total PGA inuse is more than {$ORACLE.PGA.USE.MAX.WARN}% of PGA_AGGREGATE_TARGET.</description>
+ <description>Total PGA in use is more than {$ORACLE.PGA.USE.MAX.WARN}% of PGA_AGGREGATE_TARGET.</description>
</trigger>
</triggers>
<graphs>
diff --git a/templates/db/postgresql/postgresql/pgsql.dbstat.sql b/templates/db/postgresql/postgresql/pgsql.dbstat.sql
index d874658ccca..9a6e4c4de86 100644
--- a/templates/db/postgresql/postgresql/pgsql.dbstat.sql
+++ b/templates/db/postgresql/postgresql/pgsql.dbstat.sql
@@ -14,4 +14,4 @@ SELECT json_object_agg(datname, row_to_json(T)) FROM (
temp_files,
temp_bytes,
deadlocks
- FROM pg_stat_database) T
+ FROM pg_stat_database where datname is not null) T
diff --git a/ui/app/controllers/CControllerAuthenticationEdit.php b/ui/app/controllers/CControllerAuthenticationEdit.php
index 0e0d605757b..8d6a4f9fa0d 100644
--- a/ui/app/controllers/CControllerAuthenticationEdit.php
+++ b/ui/app/controllers/CControllerAuthenticationEdit.php
@@ -87,6 +87,7 @@ class CControllerAuthenticationEdit extends CController {
protected function doAction() {
$ldap_status = (new CFrontendSetup())->checkPhpLdapModule();
+ $openssl_status = (new CFrontendSetup())->checkPhpOpenSsl();
$data = [
'action_submit' => 'authentication.update',
@@ -94,6 +95,7 @@ class CControllerAuthenticationEdit extends CController {
'ldap_error' => ($ldap_status['result'] == CFrontendSetup::CHECK_OK) ? '' : $ldap_status['error'],
'ldap_test_password' => '',
'ldap_test_user' => CWebUser::$data['alias'],
+ 'saml_error' => ($openssl_status['result'] == CFrontendSetup::CHECK_OK) ? '' : $openssl_status['error'],
'change_bind_password' => 0,
'form_refresh' => 0
];
@@ -181,6 +183,8 @@ class CControllerAuthenticationEdit extends CController {
$data['ldap_enabled'] = ($ldap_status['result'] == CFrontendSetup::CHECK_OK
&& $data['ldap_configured'] == ZBX_AUTH_LDAP_ENABLED);
+ $data['saml_enabled'] = ($openssl_status['result'] == CFrontendSetup::CHECK_OK
+ && $data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED);
$response = new CControllerResponseData($data);
$response->setTitle(_('Configuration of authentication'));
diff --git a/ui/app/controllers/CControllerAuthenticationUpdate.php b/ui/app/controllers/CControllerAuthenticationUpdate.php
index 001296a731e..e9f80dc9ae5 100644
--- a/ui/app/controllers/CControllerAuthenticationUpdate.php
+++ b/ui/app/controllers/CControllerAuthenticationUpdate.php
@@ -194,6 +194,14 @@ class CControllerAuthenticationUpdate extends CController {
* @return bool
*/
private function validateSamlAuth() {
+ $openssl_status = (new CFrontendSetup())->checkPhpOpenSsl();
+
+ if ($openssl_status['result'] != CFrontendSetup::CHECK_OK) {
+ $this->response->setMessageError($openssl_status['error']);
+
+ return false;
+ }
+
$saml_fields = ['saml_idp_entityid', 'saml_sso_url', 'saml_sp_entityid', 'saml_username_attribute'];
$saml_auth = [
'saml_idp_entityid' => CAuthenticationHelper::get(CAuthenticationHelper::SAML_IDP_ENTITYID),
@@ -237,6 +245,7 @@ class CControllerAuthenticationUpdate extends CController {
if (!$auth_valid) {
$this->response->setFormData($this->getInputAll());
$this->setResponse($this->response);
+
return;
}
@@ -245,6 +254,7 @@ class CControllerAuthenticationUpdate extends CController {
$this->response->setMessageOk(_('LDAP login successful'));
$this->response->setFormData($this->getInputAll());
$this->setResponse($this->response);
+
return;
}
diff --git a/ui/app/controllers/CControllerExport.php b/ui/app/controllers/CControllerExport.php
new file mode 100644
index 00000000000..37bd77df536
--- /dev/null
+++ b/ui/app/controllers/CControllerExport.php
@@ -0,0 +1,120 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2020 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CControllerExport extends CController {
+
+ protected function checkInput() {
+ $fields = [
+ 'action' => 'required|string',
+ 'backurl' => 'required|string',
+ 'valuemapids' => 'not_empty|array_db valuemaps.valuemapid',
+ 'hosts' => 'not_empty|array_db hosts.hostid',
+ 'mediatypeids' => 'not_empty|array_db media_type.mediatypeid',
+ 'screens' => 'not_empty|array_db screens.screenid',
+ 'maps' => 'not_empty|array_db sysmaps.sysmapid',
+ 'templates' => 'not_empty|array_db hosts.hostid',
+ 'format' => 'in '.implode(',', [CExportWriterFactory::YAML, CExportWriterFactory::XML, CExportWriterFactory::JSON])
+ ];
+
+ $ret = $this->validateInput($fields);
+
+ if (!$ret) {
+ $this->setResponse(new CControllerResponseFatal());
+ }
+
+ return $ret;
+ }
+
+ protected function checkPermissions() {
+ switch ($this->getInput('action')) {
+ case 'export.mediatypes':
+ case 'export.valuemaps':
+ return (CWebUser::$data['type'] >= USER_TYPE_SUPER_ADMIN);
+
+ case 'export.hosts':
+ case 'export.templates':
+ return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_ADMIN);
+
+ case 'export.screens':
+ case 'export.sysmaps':
+ return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_USER);
+
+ default:
+ return false;
+ }
+ }
+
+ protected function doAction() {
+ $action = $this->getInput('action');
+ $params = [
+ 'format' => $this->getInput('format', CExportWriterFactory::YAML),
+ 'prettyprint' => true,
+ 'options' => []
+ ];
+
+ switch ($action) {
+ case 'export.valuemaps':
+ $params['options']['valueMaps'] = $this->getInput('valuemapids', []);
+ break;
+
+ case 'export.hosts':
+ $params['options']['hosts'] = $this->getInput('hosts', []);
+ break;
+
+ case 'export.mediatypes':
+ $params['options']['mediaTypes'] = $this->getInput('mediatypeids', []);
+ break;
+
+ case 'export.screens':
+ $params['options']['screens'] = $this->getInput('screens', []);
+ break;
+
+ case 'export.sysmaps':
+ $params['options']['maps'] = $this->getInput('maps', []);
+ break;
+
+ case 'export.templates':
+ $params['options']['templates'] = $this->getInput('templates', []);
+ break;
+
+ default:
+ $this->setResponse(new CControllerResponseFatal());
+
+ return;
+ }
+
+ $result = API::Configuration()->export($params);
+
+ if ($result) {
+ $response = new CControllerResponseData([
+ 'main_block' => $result,
+ 'mime_type' => CExportWriterFactory::getMimeType($params['format']),
+ 'page' => ['file' => 'zbx_export_'.substr($action, 7).'.'.$params['format']]
+ ]);
+ }
+ else {
+ $response = new CControllerResponseRedirect($this->getInput('backurl', 'zabbix.php?action=dashboard.view'));
+ $response->setMessageError(_('Export failed'));
+ }
+
+ $this->setResponse($response);
+ }
+}
diff --git a/ui/app/controllers/CControllerExportXml.php b/ui/app/controllers/CControllerExportXml.php
deleted file mode 100644
index 6b1238b7008..00000000000
--- a/ui/app/controllers/CControllerExportXml.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2020 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CControllerExportXml extends CController {
-
- protected function checkInput() {
- $fields = [
- 'action' => 'required|string',
- 'backurl' => 'required|string',
- 'valuemapids' => 'not_empty|array_db valuemaps.valuemapid',
- 'hosts' => 'not_empty|array_db hosts.hostid',
- 'mediatypeids' => 'not_empty|array_db media_type.mediatypeid',
- 'screens' => 'not_empty|array_db screens.screenid',
- 'maps' => 'not_empty|array_db sysmaps.sysmapid',
- 'templates' => 'not_empty|array_db hosts.hostid'
- ];
-
- $ret = $this->validateInput($fields);
-
- if (!$ret) {
- $this->setResponse(new CControllerResponseFatal());
- }
-
- return $ret;
- }
-
- protected function checkPermissions() {
- switch ($this->getInput('action')) {
- case 'export.mediatypes.xml':
- case 'export.valuemaps.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_SUPER_ADMIN);
-
- case 'export.hosts.xml':
- case 'export.templates.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_ADMIN);
-
- case 'export.screens.xml':
- case 'export.sysmaps.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_USER);
-
- default:
- return false;
- }
- }
-
- protected function doAction() {
- $action = $this->getInput('action');
-
- switch ($action) {
- case 'export.valuemaps.xml':
- $export = new CConfigurationExport(['valueMaps' => $this->getInput('valuemapids', [])]);
- break;
-
- case 'export.hosts.xml':
- $export = new CConfigurationExport(['hosts' => $this->getInput('hosts', [])]);
- break;
-
- case 'export.mediatypes.xml':
- $export = new CConfigurationExport(['mediaTypes' => $this->getInput('mediatypeids', [])]);
- break;
-
- case 'export.screens.xml':
- $export = new CConfigurationExport(['screens' => $this->getInput('screens', [])]);
- break;
-
- case 'export.sysmaps.xml':
- $export = new CConfigurationExport(['maps' => $this->getInput('maps', [])]);
- break;
-
- case 'export.templates.xml':
- $export = new CConfigurationExport(['templates' => $this->getInput('templates', [])]);
- break;
-
- default:
- $this->setResponse(new CControllerResponseFatal());
-
- return;
- }
-
- $export->setBuilder(new CConfigurationExportBuilder());
- $export->setWriter(CExportWriterFactory::getWriter(CExportWriterFactory::XML));
-
- $export_data = $export->export();
-
- if ($export_data === false) {
- // Access denied.
-
- $response = new CControllerResponseRedirect(
- $this->getInput('backurl', 'zabbix.php?action=dashboard.view'));
-
- $response->setMessageError(_('No permissions to referred object or it does not exist!'));
- }
- else {
- $response = new CControllerResponseData([
- 'main_block' => $export_data,
- 'page' => ['file' => 'zbx_export_' . substr($action, 7)]
- ]);
- }
-
- $this->setResponse($response);
- }
-}
diff --git a/ui/app/views/administration.authentication.edit.php b/ui/app/views/administration.authentication.edit.php
index 2489607b998..b20f0914324 100644
--- a/ui/app/views/administration.authentication.edit.php
+++ b/ui/app/views/administration.authentication.edit.php
@@ -138,38 +138,38 @@ $ldap_tab = (new CFormList('list_ldap'))
);
// SAML authentication fields.
-$is_saml_auth_enabled = ($data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED);
-
$saml_tab = (new CFormList('list_saml'))
->addRow(new CLabel(_('Enable SAML authentication'), 'saml_auth_enabled'),
- (new CCheckBox('saml_auth_enabled', ZBX_AUTH_SAML_ENABLED))
- ->setChecked($is_saml_auth_enabled)
- ->setUncheckedValue(ZBX_AUTH_LDAP_DISABLED)
+ $data['saml_error']
+ ? (new CLabel($data['saml_error']))->addClass(ZBX_STYLE_RED)
+ : (new CCheckBox('saml_auth_enabled', ZBX_AUTH_SAML_ENABLED))
+ ->setChecked($data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED)
+ ->setUncheckedValue(ZBX_AUTH_SAML_DISABLED)
)
->addRow((new CLabel(_('IdP entity ID'), 'saml_idp_entityid'))->setAsteriskMark(),
(new CTextBox('saml_idp_entityid', $data['saml_idp_entityid'], false,
DB::getFieldLength('config', 'saml_idp_entityid')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
->addRow((new CLabel(_('SSO service URL'), 'saml_sso_url'))->setAsteriskMark(),
(new CTextBox('saml_sso_url', $data['saml_sso_url'], false, DB::getFieldLength('config', 'saml_sso_url')))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
->addRow(new CLabel(_('SLO service URL'), 'saml_slo_url'),
(new CTextBox('saml_slo_url', $data['saml_slo_url'], false, DB::getFieldLength('config', 'saml_slo_url')))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
)
->addRow((new CLabel(_('Username attribute'), 'saml_username_attribute'))->setAsteriskMark(),
(new CTextBox('saml_username_attribute', $data['saml_username_attribute'], false,
DB::getFieldLength('config', 'saml_username_attribute')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
@@ -177,7 +177,7 @@ $saml_tab = (new CFormList('list_saml'))
(new CTextBox('saml_sp_entityid', $data['saml_sp_entityid'], false,
DB::getFieldLength('config', 'saml_sp_entityid')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
@@ -185,7 +185,7 @@ $saml_tab = (new CFormList('list_saml'))
(new CTextBox('saml_nameid_format', $data['saml_nameid_format'], false,
DB::getFieldLength('config', 'saml_nameid_format')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAttribute('placeholder', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')
)
@@ -196,31 +196,31 @@ $saml_tab = (new CFormList('list_saml'))
->setLabel(_('Messages'))
->setChecked($data['saml_sign_messages'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_assertions'))
->setLabel(_('Assertions'))
->setChecked($data['saml_sign_assertions'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_authn_requests'))
->setLabel(_('AuthN requests'))
->setChecked($data['saml_sign_authn_requests'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_logout_requests'))
->setLabel(_('Logout requests'))
->setChecked($data['saml_sign_logout_requests'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_logout_responses'))
->setLabel(_('Logout responses'))
->setChecked($data['saml_sign_logout_responses'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
)
->addRow(_('Encrypt'),
@@ -230,20 +230,20 @@ $saml_tab = (new CFormList('list_saml'))
->setLabel(_('Name ID'))
->setChecked($data['saml_encrypt_nameid'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_encrypt_assertions'))
->setLabel(_('Assertions'))
->setChecked($data['saml_encrypt_assertions'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
)
->addRow(new CLabel(_('Case sensitive login'), 'saml_case_sensitive'),
(new CCheckBox('saml_case_sensitive'))
->setChecked($data['saml_case_sensitive'] == ZBX_AUTH_CASE_SENSITIVE)
->setUncheckedValue(ZBX_AUTH_CASE_INSENSITIVE)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
);
(new CWidget())
diff --git a/ui/app/views/administration.mediatype.list.php b/ui/app/views/administration.mediatype.list.php
index d812ef30675..6f8aa6cec7e 100644
--- a/ui/app/views/administration.mediatype.list.php
+++ b/ui/app/views/administration.mediatype.list.php
@@ -161,14 +161,13 @@ $mediaTypeForm->addItem([
new CActionButtonList('action', 'mediatypeids', [
'mediatype.enable' => ['name' => _('Enable'), 'confirm' => _('Enable selected media types?')],
'mediatype.disable' => ['name' => _('Disable'), 'confirm' => _('Disable selected media types?')],
- 'mediatype.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.mediatypes.xml')
- ->setArgument('backurl', (new CUrl('zabbix.php'))
+ 'mediatype.export' => [
+ 'content' => new CButtonExport('export.mediatypes',
+ (new CUrl('zabbix.php'))
->setArgument('action', 'mediatype.list')
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'mediatype.delete' => ['name' => _('Delete'), 'confirm' => _('Delete selected media types?')]
], 'mediatype')
diff --git a/ui/app/views/administration.valuemap.list.php b/ui/app/views/administration.valuemap.list.php
index df4bb0d9803..1184af2060a 100644
--- a/ui/app/views/administration.valuemap.list.php
+++ b/ui/app/views/administration.valuemap.list.php
@@ -87,14 +87,13 @@ $form->addItem([
$table,
$data['paging'],
new CActionButtonList('action', 'valuemapids', [
- 'valuemap.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.valuemaps.xml')
- ->setArgument('backurl', (new CUrl('zabbix.php'))
+ 'valuemap.export' => [
+ 'content' => new CButtonExport('export.valuemaps',
+ (new CUrl('zabbix.php'))
->setArgument('action', 'valuemap.list')
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'valuemap.delete' => ['name' => _('Delete'), 'confirm' => _('Delete selected value maps?')]
])
diff --git a/ui/app/views/layout.xml.php b/ui/app/views/layout.export.php
index abf37862b37..38a1b1d88fd 100644
--- a/ui/app/views/layout.xml.php
+++ b/ui/app/views/layout.export.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-header('Content-Type: text/xml; charset=utf-8');
+header('Content-Type: '.$data['mime_type'].'; charset=utf-8');
header('Content-Disposition: attachment; filename="'.$data['page']['file'].'"');
echo $data['main_block'];
diff --git a/ui/assets/styles/blue-theme.css b/ui/assets/styles/blue-theme.css
index 24b145d6d29..0808a1cb775 100644
--- a/ui/assets/styles/blue-theme.css
+++ b/ui/assets/styles/blue-theme.css
@@ -5440,6 +5440,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #ffffff;
+ background-color: #02659f;
+ border-color: #02659f; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #0275b8;
+ border-right: 1px solid #0275b8;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #acbbc2;
+ border-right-color: #acbbc2; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #ffffff;
+ border-right-color: #ffffff; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5454,10 +5496,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/dark-theme.css b/ui/assets/styles/dark-theme.css
index 05798dc720c..0671789e72f 100644
--- a/ui/assets/styles/dark-theme.css
+++ b/ui/assets/styles/dark-theme.css
@@ -5451,6 +5451,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #f2f2f2;
+ background-color: #5e737e;
+ border-color: #5e737e; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #768d99;
+ border-right: 1px solid #768d99;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #525252;
+ border-right-color: #525252; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #f2f2f2;
+ border-right-color: #f2f2f2; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5465,10 +5507,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/hc-dark.css b/ui/assets/styles/hc-dark.css
index db2aaffefc7..de0e2fa8ac8 100644
--- a/ui/assets/styles/hc-dark.css
+++ b/ui/assets/styles/hc-dark.css
@@ -5395,6 +5395,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #333333;
+ background-color: lightgray;
+ border-color: lightgray; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #ffffff;
+ border-right: 1px solid #ffffff;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #7d7d7d;
+ border-right-color: #7d7d7d; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #333333;
+ border-right-color: #333333; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5409,10 +5451,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/hc-light.css b/ui/assets/styles/hc-light.css
index 492fc127eee..d8552e123bb 100644
--- a/ui/assets/styles/hc-light.css
+++ b/ui/assets/styles/hc-light.css
@@ -5395,6 +5395,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #ffffff;
+ background-color: #484848;
+ border-color: #484848; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #000000;
+ border-right: 1px solid #000000;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #999999;
+ border-right-color: #999999; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #ffffff;
+ border-right-color: #ffffff; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5409,10 +5451,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/host_discovery.php b/ui/host_discovery.php
index 2e53f7ae9b0..5bca459fd24 100644
--- a/ui/host_discovery.php
+++ b/ui/host_discovery.php
@@ -815,7 +815,7 @@ else {
// Select LLD rules.
$options = [
'output' => API_OUTPUT_EXTEND,
- 'selectHosts' => ['hostid', 'name', 'status'],
+ 'selectHosts' => ['hostid', 'name', 'status', 'flags'],
'selectItems' => API_OUTPUT_COUNT,
'selectGraphs' => API_OUTPUT_COUNT,
'selectTriggers' => API_OUTPUT_COUNT,
diff --git a/ui/hostgroups.php b/ui/hostgroups.php
index 53e07e10594..6bf8dc209a6 100644
--- a/ui/hostgroups.php
+++ b/ui/hostgroups.php
@@ -160,7 +160,6 @@ elseif (hasRequest('action')) {
elseif (getRequest('action') == 'hostgroup.massenable' || getRequest('action') == 'hostgroup.massdisable') {
$enable = (getRequest('action') == 'hostgroup.massenable');
$status = $enable ? HOST_STATUS_MONITORED : HOST_STATUS_NOT_MONITORED;
- $auditAction = $enable ? AUDIT_ACTION_ENABLE : AUDIT_ACTION_DISABLE;
$groupIds = getRequest('groups', []);
@@ -180,14 +179,6 @@ elseif (hasRequest('action')) {
'hosts' => $hosts,
'status' => $status
]);
-
- if ($result) {
- foreach ($hosts as $host) {
- add_audit_ext($auditAction, AUDIT_RESOURCE_HOST, $host['hostid'], $host['host'], 'hosts',
- ['status' => $host['status']], ['status' => $status]
- );
- }
- }
}
$result = DBend($result);
diff --git a/ui/hosts.php b/ui/hosts.php
index 74e360f6484..d1bed15beff 100644
--- a/ui/hosts.php
+++ b/ui/hosts.php
@@ -839,16 +839,9 @@ elseif (hasRequest('add') || hasRequest('update')) {
}
if ($create) {
- $hostIds = API::Host()->create($host);
-
- if ($hostIds) {
- $hostId = reset($hostIds['hostids']);
- }
- else {
+ if (!API::Host()->create($host)) {
throw new Exception();
}
-
- add_audit_ext(AUDIT_ACTION_ADD, AUDIT_RESOURCE_HOST, $hostId, $host['host'], null, null, null);
}
else {
$host['hostid'] = $hostId;
@@ -856,16 +849,6 @@ elseif (hasRequest('add') || hasRequest('update')) {
if (!API::Host()->update($host)) {
throw new Exception();
}
-
- $dbHostNew = API::Host()->get([
- 'output' => API_OUTPUT_EXTEND,
- 'hostids' => $hostId,
- 'editable' => true
- ]);
- $dbHostNew = reset($dbHostNew);
-
- add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $dbHostNew['hostid'], $dbHostNew['host'], 'hosts',
- $dbHost, $dbHostNew);
}
// full clone
@@ -1001,13 +984,14 @@ elseif (hasRequest('hosts') && hasRequest('action') && str_in_array(getRequest('
'templated_hosts' => true,
'output' => ['hostid']
]);
- $actHosts = zbx_objectValues($actHosts, 'hostid');
if ($actHosts) {
- DBstart();
+ foreach ($actHosts as &$host) {
+ $host['status'] = $status;
+ }
+ unset($host);
- $result = updateHostStatus($actHosts, $status);
- $result = DBend($result);
+ $result = (bool) API::Host()->update($actHosts);
if ($result) {
uncheckTableRows();
diff --git a/ui/include/classes/api/CAudit.php b/ui/include/classes/api/CAudit.php
index b7fbee571ed..c12fb3dddf1 100644
--- a/ui/include/classes/api/CAudit.php
+++ b/ui/include/classes/api/CAudit.php
@@ -36,6 +36,7 @@ class CAudit {
AUDIT_RESOURCE_DISCOVERY_RULE => ['druleid', 'name', 'drules'],
AUDIT_RESOURCE_GRAPH => ['graphid', 'name', 'graphs'],
AUDIT_RESOURCE_GRAPH_PROTOTYPE => ['graphid', 'name', 'graphs'],
+ AUDIT_RESOURCE_HOST => ['hostid', 'name', 'hosts'],
AUDIT_RESOURCE_HOST_GROUP => ['groupid', 'name', 'groups'],
AUDIT_RESOURCE_HOST_PROTOTYPE => ['hostid', 'host', 'hosts'],
AUDIT_RESOURCE_HOUSEKEEPING => ['configid', null, 'config'],
diff --git a/ui/include/classes/api/services/CConfiguration.php b/ui/include/classes/api/services/CConfiguration.php
index 6bcb32006b4..f88b0940205 100644
--- a/ui/include/classes/api/services/CConfiguration.php
+++ b/ui/include/classes/api/services/CConfiguration.php
@@ -31,7 +31,8 @@ class CConfiguration extends CApiService {
*/
public function export(array $params) {
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CExportWriterFactory::XML, CExportWriterFactory::JSON])],
+ 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CExportWriterFactory::YAML, CExportWriterFactory::XML, CExportWriterFactory::JSON])],
+ 'prettyprint' => ['type' => API_BOOLEAN, 'default' => false],
'options' => ['type' => API_OBJECT, 'flags' => API_REQUIRED, 'fields' => [
'groups' => ['type' => API_IDS],
'hosts' => ['type' => API_IDS],
@@ -47,10 +48,34 @@ class CConfiguration extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ switch ($params['format']) {
+ case CExportWriterFactory::YAML:
+ $lib_yaml = (new CFrontendSetup())->checkPhpLibYAML();
+
+ if ($lib_yaml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_yaml['error']);
+ }
+ break;
+
+ case CExportWriterFactory::XML:
+ $lib_xml = (new CFrontendSetup())->checkPhpLibxml();
+
+ if ($lib_xml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_xml['error']);
+ }
+
+ $xml_writer = (new CFrontendSetup())->checkPhpXmlWriter();
+
+ if ($xml_writer['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $xml_writer['error']);
+ }
+ break;
+ }
+
$export = new CConfigurationExport($params['options']);
$export->setBuilder(new CConfigurationExportBuilder());
$writer = CExportWriterFactory::getWriter($params['format']);
- $writer->formatOutput(false);
+ $writer->formatOutput($params['prettyprint']);
$export->setWriter($writer);
$export_data = $export->export();
@@ -69,7 +94,7 @@ class CConfiguration extends CApiService {
*/
public function import($params) {
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CImportReaderFactory::XML, CImportReaderFactory::JSON])],
+ 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CImportReaderFactory::YAML, CImportReaderFactory::XML, CImportReaderFactory::JSON])],
'source' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
'rules' => ['type' => API_OBJECT, 'flags' => API_REQUIRED, 'fields' => [
'applications' => ['type' => API_OBJECT, 'fields' => [
@@ -147,6 +172,30 @@ class CConfiguration extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ switch ($params['format']) {
+ case CImportReaderFactory::YAML:
+ $lib_yaml = (new CFrontendSetup())->checkPhpLibYAML();
+
+ if ($lib_yaml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_yaml['error']);
+ }
+ break;
+
+ case CImportReaderFactory::XML:
+ $lib_xml = (new CFrontendSetup())->checkPhpLibxml();
+
+ if ($lib_xml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_xml['error']);
+ }
+
+ $xml_reader = (new CFrontendSetup())->checkPhpXmlReader();
+
+ if ($xml_reader['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $xml_reader['error']);
+ }
+ break;
+ }
+
$importReader = CImportReaderFactory::getReader($params['format']);
$data = $importReader->read($params['source']);
@@ -177,8 +226,8 @@ class CConfiguration extends CApiService {
// Add default values in place of missed tags.
$data = (new CDefaultImportConverter($schema))->convert($data);
- // Normalize array keys.
- $data = (new CArrayKeysImportConverter($schema))->convert($data);
+ // Normalize array keys and strings.
+ $data = (new CImportDataNormalizer($schema))->normalize($data);
// Transform converter.
$data = (new CTransformImportConverter($schema))->convert($data);
diff --git a/ui/include/classes/api/services/CHost.php b/ui/include/classes/api/services/CHost.php
index cacba3db1b9..00f0bb06a44 100644
--- a/ui/include/classes/api/services/CHost.php
+++ b/ui/include/classes/api/services/CHost.php
@@ -700,7 +700,7 @@ class CHost extends CHostGeneral {
$hostids = [];
$ins_tags = [];
- foreach ($hosts as $host) {
+ foreach ($hosts as &$host) {
// If visible name is not given or empty it should be set to host name.
if (!array_key_exists('name', $host) || !trim($host['name'])) {
$host['name'] = $host['host'];
@@ -768,6 +768,9 @@ class CHost extends CHostGeneral {
DB::insert('host_inventory', [$hostInventory], false);
}
}
+ unset($host);
+
+ $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_HOST, $hosts);
if ($ins_tags) {
DB::insert('host_tag', $ins_tags);
@@ -971,7 +974,10 @@ class CHost extends CHostGeneral {
sort($hostids);
$db_hosts = $this->get([
- 'output' => ['hostid', 'host'],
+ 'output' => ['hostid', 'proxy_hostid', 'host', 'status', 'ipmi_authtype', 'ipmi_privilege', 'ipmi_username',
+ 'ipmi_password', 'name', 'description', 'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject',
+ 'tls_psk_identity', 'tls_psk', 'inventory_mode'
+ ],
'hostids' => $hostids,
'editable' => true,
'preservekeys' => true
@@ -1117,12 +1123,8 @@ class CHost extends CHostGeneral {
$updateInventory['inventory_mode'] = $data['inventory_mode'];
}
- if (isset($data['status'])) {
- $updateStatus = $data['status'];
- }
-
unset($data['hosts'], $data['groups'], $data['interfaces'], $data['templates_clear'], $data['templates'],
- $data['macros'], $data['inventory'], $data['inventory_mode'], $data['status']);
+ $data['macros'], $data['inventory'], $data['inventory_mode']);
if (!zbx_empty($data)) {
DB::update('hosts', [
@@ -1131,10 +1133,6 @@ class CHost extends CHostGeneral {
]);
}
- if (isset($updateStatus)) {
- updateHostStatus($hostids, $updateStatus);
- }
-
/*
* Update template linkage
*/
@@ -1326,6 +1324,18 @@ class CHost extends CHostGeneral {
}
}
+ $new_hosts = [];
+ foreach ($db_hosts as $hostid => $db_host) {
+ $new_host = $data + $db_host;
+ if ($new_host['status'] != $db_host['status']) {
+ info(_s('Updated status of host "%1$s".', $new_host['host']));
+ }
+
+ $new_hosts[] = $new_host;
+ }
+
+ $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $new_hosts, $db_hosts);
+
return ['hostids' => $inputHostIds];
}
diff --git a/ui/include/classes/export/CConfigurationExport.php b/ui/include/classes/export/CConfigurationExport.php
index da061e0ca18..86ab460560e 100644
--- a/ui/include/classes/export/CConfigurationExport.php
+++ b/ui/include/classes/export/CConfigurationExport.php
@@ -136,7 +136,8 @@ class CConfigurationExport {
try {
$this->gatherData();
- $schema = (new CImportValidatorFactory('xml'))
+ // Parameter in CImportValidatorFactory is irrelavant here, since export does not validate data.
+ $schema = (new CImportValidatorFactory(CExportWriterFactory::YAML))
->getObject(ZABBIX_EXPORT_VERSION)
->getSchema();
@@ -174,7 +175,7 @@ class CConfigurationExport {
}
if ($this->data['maps']) {
- $this->builder->buildMaps($this->data['maps']);
+ $this->builder->buildMaps($schema['rules']['maps'], $this->data['maps']);
}
if ($this->data['mediaTypes']) {
@@ -1442,10 +1443,14 @@ class CConfigurationExport {
break;
}
- $selement['iconid_off'] = $selement['iconid_off'] > 0 ? $images[$selement['iconid_off']] : '';
- $selement['iconid_on'] = $selement['iconid_on'] > 0 ? $images[$selement['iconid_on']] : '';
- $selement['iconid_disabled'] = $selement['iconid_disabled'] > 0 ? $images[$selement['iconid_disabled']] : '';
- $selement['iconid_maintenance'] = $selement['iconid_maintenance'] > 0 ? $images[$selement['iconid_maintenance']] : '';
+ $selement['iconid_off'] = ($selement['iconid_off'] > 0) ? $images[$selement['iconid_off']] : [];
+ $selement['iconid_on'] = ($selement['iconid_on'] > 0) ? $images[$selement['iconid_on']] : [];
+ $selement['iconid_disabled'] = ($selement['iconid_disabled'] > 0)
+ ? $images[$selement['iconid_disabled']]
+ : [];
+ $selement['iconid_maintenance'] = ($selement['iconid_maintenance'] > 0)
+ ? $images[$selement['iconid_maintenance']]
+ : [];
}
unset($selement);
diff --git a/ui/include/classes/export/CConfigurationExportBuilder.php b/ui/include/classes/export/CConfigurationExportBuilder.php
index 0e0596df9f4..b69458660c3 100644
--- a/ui/include/classes/export/CConfigurationExportBuilder.php
+++ b/ui/include/classes/export/CConfigurationExportBuilder.php
@@ -44,11 +44,11 @@ class CConfigurationExportBuilder {
}
/**
- * Build XML data.
+ * Build data structure.
*
* @param array $schema Tag schema from validation class.
* @param array $data Export data.
- * @param string $main_tag XML tag (for error reporting).
+ * @param string $main_tag Main element (for error reporting).
*
* @return array
*/
@@ -66,6 +66,7 @@ class CConfigurationExportBuilder {
$store = [];
foreach ($rules as $tag => $val) {
$is_required = $val['type'] & XML_REQUIRED;
+ $is_string = $val['type'] & XML_STRING;
$is_array = $val['type'] & XML_ARRAY;
$is_indexed_array = $val['type'] & XML_INDEXED_ARRAY;
$has_data = array_key_exists($tag, $row);
@@ -99,6 +100,10 @@ class CConfigurationExportBuilder {
continue;
}
+ if ($is_string && $value !== null) {
+ $value = str_replace("\r\n", "\n", $value);
+ }
+
if (array_key_exists('in', $val)) {
if (!array_key_exists($value, $val['in'])) {
throw new Exception(_s('Invalid tag "%1$s": %2$s.', $tag,
@@ -338,9 +343,10 @@ class CConfigurationExportBuilder {
/**
* Format maps.
*
- * @param array $maps
+ * @param array $schema Tag schema from validation class.
+ * @param array $maps Export data.
*/
- public function buildMaps(array $maps) {
+ public function buildMaps(array $schema, array $maps) {
$this->data['maps'] = [];
CArrayHelper::sort($maps, ['name']);
@@ -383,6 +389,8 @@ class CConfigurationExportBuilder {
'links' => $this->formatMapLinks($map['links'], $tmpSelements)
];
}
+
+ $this->data['maps'] = $this->build($schema, $this->data['maps'], 'maps');
}
/**
diff --git a/ui/include/classes/export/writers/CExportWriterFactory.php b/ui/include/classes/export/writers/CExportWriterFactory.php
index ed36c368f9f..d2e9235c70b 100644
--- a/ui/include/classes/export/writers/CExportWriterFactory.php
+++ b/ui/include/classes/export/writers/CExportWriterFactory.php
@@ -21,6 +21,7 @@
class CExportWriterFactory {
+ const YAML = 'yaml';
const XML = 'xml';
const JSON = 'json';
@@ -36,6 +37,9 @@ class CExportWriterFactory {
*/
public static function getWriter($type) {
switch ($type) {
+ case self::YAML:
+ return new CYamlExportWriter();
+
case self::XML:
return new CXmlExportWriter();
@@ -46,4 +50,33 @@ class CExportWriterFactory {
throw new Exception('Incorrect export writer type.');
}
}
+
+ /**
+ * Get content mime-type for specified type.
+ *
+ * @static
+ * @throws Exception
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function getMimeType(string $type): string {
+ switch ($type) {
+ case self::YAML:
+ // See https://github.com/rails/rails/blob/d41d586/actionpack/lib/action_dispatch/http/mime_types.rb#L39
+ return 'text/yaml';
+
+ case self::XML:
+ // See https://www.ietf.org/rfc/rfc2376.txt
+ return 'text/xml';
+
+ case self::JSON:
+ // See https://www.ietf.org/rfc/rfc4627.txt
+ return 'application/json';
+
+ default:
+ throw new Exception('Incorrect export writer type.');
+ }
+ }
}
diff --git a/ui/include/classes/export/writers/CJsonExportWriter.php b/ui/include/classes/export/writers/CJsonExportWriter.php
index 3c2d530b36b..562b7dc4b75 100644
--- a/ui/include/classes/export/writers/CJsonExportWriter.php
+++ b/ui/include/classes/export/writers/CJsonExportWriter.php
@@ -32,6 +32,12 @@ class CJsonExportWriter extends CExportWriter {
* @return string
*/
public function write(array $array) {
- return json_encode($array, JSON_UNESCAPED_SLASHES);
+ $options = JSON_UNESCAPED_SLASHES;
+
+ if ($this->formatOutput) {
+ $options |= JSON_PRETTY_PRINT;
+ }
+
+ return json_encode($array, $options);
}
}
diff --git a/ui/include/classes/export/writers/CYamlExportWriter.php b/ui/include/classes/export/writers/CYamlExportWriter.php
new file mode 100644
index 00000000000..13b0f2cc140
--- /dev/null
+++ b/ui/include/classes/export/writers/CYamlExportWriter.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for converting array with export data to YAML format.
+ */
+class CYamlExportWriter extends CExportWriter {
+
+ /**
+ * Converts array with export data to YAML format.
+ *
+ * @param array $array
+ *
+ * @return string
+ */
+ public function write(array $array): string {
+ return yaml_emit($array, YAML_UTF8_ENCODING, YAML_LN_BREAK);
+ }
+}
diff --git a/ui/include/classes/html/CActionButtonList.php b/ui/include/classes/html/CActionButtonList.php
index f3c0b79fdba..e5d7ae9eab7 100644
--- a/ui/include/classes/html/CActionButtonList.php
+++ b/ui/include/classes/html/CActionButtonList.php
@@ -62,6 +62,7 @@ class CActionButtonList extends CObject {
* @param string $buttons_data[]['confirm'] Confirmation text (optional).
* @param string $buttons_data[]['redirect'] Redirect URL (optional).
* @param bool $buttons_data[]['disabled'] Set button state disabled (optional).
+ * @param CTag $buttons_data[]['content'] A HTML tag. For example a CButton wrapped in CList object.
* @param string|null $name_prefix Prefix for sessionStorage used for storing currently selected
* checkboxes.
*/
@@ -70,42 +71,47 @@ class CActionButtonList extends CObject {
$this->name_prefix = $name_prefix ? $name_prefix : null;
foreach ($buttons_data as $action => $button_data) {
- $button = (new CSubmit($action_name, $button_data['name']))
- ->addClass(ZBX_STYLE_BTN_ALT)
- ->removeAttribute('id');
-
- if (array_key_exists('redirect', $button_data)) {
- $button
- // Removing parameters not to conflict with the redirecting URL.
- ->removeAttribute('name')
- ->removeAttribute('value')
- ->onClick('var $_form = jQuery(this).closest("form");'.
- // Save the original form action.
- 'if (!$_form.data("action")) {'.
- '$_form.data("action", $_form.attr("action"));'.
- '}'.
- '$_form.attr("action", '.json_encode($button_data['redirect']).');'
- );
+ if (array_key_exists('content', $button_data)) {
+ $button = $button_data['content'];
}
else {
- $button
- ->setAttribute('value', $action)
- ->onClick('var $_form = jQuery(this).closest("form");'.
- // Restore the original form action, if previously saved.
- 'if ($_form.data("action")) {'.
- '$_form.attr("action", $_form.data("action"));'.
- '}'
- );
- }
-
- if (array_key_exists('disabled', $button_data)) {
- $button
- ->setEnabled(!$button_data['disabled'])
- ->setAttribute('data-disabled', $button_data['disabled']);
- }
-
- if (array_key_exists('confirm', $button_data)) {
- $button->setAttribute('confirm', $button_data['confirm']);
+ $button = (new CSubmit($action_name, $button_data['name']))
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->removeAttribute('id');
+
+ if (array_key_exists('redirect', $button_data)) {
+ $button
+ // Removing parameters not to conflict with the redirecting URL.
+ ->removeAttribute('name')
+ ->removeAttribute('value')
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Save the original form action.
+ 'if (!$_form.data("action")) {'.
+ '$_form.data("action", $_form.attr("action"));'.
+ '}'.
+ '$_form.attr("action", '.json_encode($button_data['redirect']).');'
+ );
+ }
+ else {
+ $button
+ ->setAttribute('value', $action)
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Restore the original form action, if previously saved.
+ 'if ($_form.data("action")) {'.
+ '$_form.attr("action", $_form.data("action"));'.
+ '}'
+ );
+ }
+
+ if (array_key_exists('disabled', $button_data)) {
+ $button
+ ->setEnabled(!$button_data['disabled'])
+ ->setAttribute('data-disabled', $button_data['disabled']);
+ }
+
+ if (array_key_exists('confirm', $button_data)) {
+ $button->setAttribute('confirm', $button_data['confirm']);
+ }
}
$this->buttons[$action] = $button;
diff --git a/ui/include/classes/html/CButtonDropdown.php b/ui/include/classes/html/CButtonDropdown.php
index b90f50e225b..4ca0518716d 100644
--- a/ui/include/classes/html/CButtonDropdown.php
+++ b/ui/include/classes/html/CButtonDropdown.php
@@ -29,7 +29,6 @@ class CButtonDropdown extends CButton {
/**
* Button style names.
*/
- public const ZBX_STYLE_BTN_TOGGLE = 'btn-dropdown-toggle';
public const ZBX_STYLE_BTN_VALUE = 'dropdown-value';
/**
@@ -54,7 +53,7 @@ class CButtonDropdown extends CButton {
$this->setId(uniqid('btn-dropdown-'));
$this->addClass(ZBX_STYLE_BTN_ALT);
- $this->addClass(self::ZBX_STYLE_BTN_TOGGLE);
+ $this->addClass(ZBX_STYLE_BTN_TOGGLE);
$this->dropdown_items = $items;
if ($value !== null) {
@@ -74,7 +73,10 @@ class CButtonDropdown extends CButton {
->setId(zbx_formatDomId($name.'[btn]'))
->setMenuPopup([
'type' => 'dropdown',
- 'data' => ['items' => $this->dropdown_items]
+ 'data' => [
+ 'items' => $this->dropdown_items,
+ 'toggle_class' => ZBX_STYLE_BTN_TOGGLE
+ ]
]))
->addItem((new CInput('hidden', $name, $this->getAttribute('value')))
->addClass(self::ZBX_STYLE_BTN_VALUE)
diff --git a/ui/include/classes/html/CButtonExport.php b/ui/include/classes/html/CButtonExport.php
new file mode 100644
index 00000000000..093efb78a45
--- /dev/null
+++ b/ui/include/classes/html/CButtonExport.php
@@ -0,0 +1,89 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CButtonExport extends CList {
+
+ /**
+ * Create CButtonExport instance.
+ *
+ * @param string $action Export controller action.
+ * @param string $back_url URL to redirect back to once export is complete.
+ */
+ public function __construct(string $action, string $back_url) {
+ parent::__construct([
+ (new CSubmit('export', _('Export')))
+ ->removeAttribute('id')
+ ->removeAttribute('name')
+ ->removeAttribute('value')
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Save the original form action.
+ 'if (!$_form.data("action")) {'.
+ '$_form.data("action", $_form.attr("action"));'.
+ '}'.
+ '$_form.attr("action", '.json_encode(
+ (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::YAML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ).');'
+ ),
+ (new CButton('export', '&#8203;'))
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->addClass(ZBX_STYLE_BTN_TOGGLE_CHEVRON)
+ ->setMenuPopup([
+ 'type' => 'dropdown',
+ 'data' => [
+ 'submit_form' => true,
+ 'items' => [
+ [
+ 'label' => _('YAML'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::YAML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ],
+ [
+ 'label' => _('XML'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::XML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ],
+ [
+ 'label' => _('JSON'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::JSON)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ]
+ ]
+ ]
+ ])
+ ]);
+
+ $this->addClass(ZBX_STYLE_BTN_SPLIT);
+ }
+}
diff --git a/ui/include/classes/import/converters/CArrayKeysImportConverter.php b/ui/include/classes/import/converters/CImportDataNormalizer.php
index ddaf6c8f657..b0a57683ded 100644
--- a/ui/include/classes/import/converters/CArrayKeysImportConverter.php
+++ b/ui/include/classes/import/converters/CImportDataNormalizer.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types=1);
/*
** Zabbix
** Copyright (C) 2001-2020 Zabbix SIA
@@ -20,18 +20,21 @@
/**
- * Convert array keys to numeric.
+ * Class to normalize incoming data.
*/
-class CArrayKeysImportConverter extends CConverter {
+class CImportDataNormalizer {
protected $rules;
+ const EOL_LF = 0x01;
+
public function __construct(array $schema) {
$this->rules = $schema;
}
- public function convert($data) {
+ public function normalize($data) {
$data['zabbix_export'] = $this->normalizeArrayKeys($data['zabbix_export'], $this->rules);
+ $data['zabbix_export'] = $this->normalizeStrings($data['zabbix_export']);
return $data;
}
@@ -40,9 +43,9 @@ class CArrayKeysImportConverter extends CConverter {
* Convert array keys to numeric.
*
* @param mixed $data Import data.
- * @param array $rules XML rules.
+ * @param array $rules Schema rules.
*
- * @return array
+ * @return mixed
*/
protected function normalizeArrayKeys($data, array $rules) {
if (!is_array($data)) {
@@ -72,4 +75,22 @@ class CArrayKeysImportConverter extends CConverter {
return $data;
}
+
+ /**
+ * Add CR to string type fields.
+ *
+ * @param mixed $data Import data.
+ *
+ * @return mixed
+ */
+ protected function normalizeStrings($data) {
+ if ($this->rules['type'] & XML_STRING) {
+ $data = str_replace("\r\n", "\n", $data);
+ $data = (array_key_exists('flags', $this->rules) && $this->rules['flags'] & self::EOL_LF)
+ ? $data
+ : str_replace("\n", "\r\n", $data);
+ }
+
+ return $data;
+ }
}
diff --git a/ui/include/classes/import/importers/CMapImporter.php b/ui/include/classes/import/importers/CMapImporter.php
index 2601eae46d7..a2226477e19 100644
--- a/ui/include/classes/import/importers/CMapImporter.php
+++ b/ui/include/classes/import/importers/CMapImporter.php
@@ -187,7 +187,7 @@ class CMapImporter extends CImporter {
'icon_off' => 'iconid_off',
'icon_on' => 'iconid_on',
'icon_disabled' => 'iconid_disabled',
- 'icon_maintenance' => 'iconid_maintenance',
+ 'icon_maintenance' => 'iconid_maintenance'
];
foreach ($icons as $element => $field) {
if (array_key_exists($element, $selement)) {
diff --git a/ui/include/classes/import/readers/CImportReaderFactory.php b/ui/include/classes/import/readers/CImportReaderFactory.php
index fff3cc4fda0..56bf1a0bd9c 100644
--- a/ui/include/classes/import/readers/CImportReaderFactory.php
+++ b/ui/include/classes/import/readers/CImportReaderFactory.php
@@ -21,6 +21,7 @@
class CImportReaderFactory {
+ const YAML = 'yaml';
const XML = 'xml';
const JSON = 'json';
@@ -36,10 +37,15 @@ class CImportReaderFactory {
*/
public static function getReader($format) {
switch ($format) {
- case 'xml':
+ case self::YAML:
+ return new CYamlImportReader();
+
+ case self::XML:
return new CXmlImportReader();
- case 'json':
+
+ case self::JSON:
return new CJsonImportReader();
+
default:
throw new Exception(_s('Unsupported import format "%1$s".', $format));
}
@@ -57,13 +63,18 @@ class CImportReaderFactory {
*/
public static function fileExt2ImportFormat($ext) {
switch ($ext) {
+ case 'yaml':
+ case 'yml':
+ return CImportReaderFactory::YAML;
+
case 'xml':
return CImportReaderFactory::XML;
+
case 'json':
return CImportReaderFactory::JSON;
+
default:
throw new Exception(_s('Unsupported import file extension "%1$s".', $ext));
}
-
}
}
diff --git a/ui/include/classes/import/readers/CYamlImportReader.php b/ui/include/classes/import/readers/CYamlImportReader.php
new file mode 100644
index 00000000000..8aad995f897
--- /dev/null
+++ b/ui/include/classes/import/readers/CYamlImportReader.php
@@ -0,0 +1,66 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for converting YAML data stream to PHP array.
+ */
+class CYamlImportReader extends CImportReader {
+
+ /**
+ * Convert YAML data stream to PHP array. Suppress PHP notices when executing yaml_parse() with custom
+ * error handler. Display only first error since that is where the syntax in file is incorrect.
+ *
+ * @param string $string
+ *
+ * @throws ErrorException
+ *
+ * @return array
+ */
+ public function read($string): array {
+ $error = '';
+
+ set_error_handler(function ($errno, $errstr) use (&$error) {
+ if ($error === '' && $errstr !== '') {
+ $error = str_replace('yaml_parse(): ', '', $errstr);
+ }
+ });
+
+ $data = yaml_parse($string);
+
+ restore_error_handler();
+
+ /*
+ * Unfortunately yaml_parse() not always returns FALSE. If file is empty, it returns NULL and if file contains
+ * gibberish and not a "zabbix_export" array, $data contains same input string, but Import Validator expects
+ * $data to be an array. Create a custom error message for these cases.
+ */
+ if (!is_array($data) && $data !== false) {
+ $data = false;
+ $error = _('Invalid file content');
+ }
+
+ if ($data === false) {
+ throw new ErrorException(_s('Cannot read YAML: %1$s.', $error));
+ }
+
+ return $data;
+ }
+}
diff --git a/ui/include/classes/import/validators/C50XmlValidator.php b/ui/include/classes/import/validators/C50XmlValidator.php
index 7b9a8653c36..43c7965067b 100644
--- a/ui/include/classes/import/validators/C50XmlValidator.php
+++ b/ui/include/classes/import/validators/C50XmlValidator.php
@@ -457,7 +457,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -598,7 +598,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -811,7 +811,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE_DRULE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1068,7 +1068,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1207,7 +1207,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1418,7 +1418,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE_DRULE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -2259,7 +2259,7 @@ class C50XmlValidator {
switch ($data['type']) {
case CXmlConstantName::SCRIPT:
case CXmlConstantValue::MEDIA_TYPE_SCRIPT:
- return ['type' => XML_STRING, 'default' => '', 'preprocessor' => [$this, 'scriptParameterPreprocessor'], 'export' => [$this, 'scriptParameterExport']];
+ return ['type' => XML_STRING, 'flags' => CImportDataNormalizer::EOL_LF, 'default' => '', 'preprocessor' => [$this, 'scriptParameterPreprocessor'], 'export' => [$this, 'scriptParameterExport']];
case CXmlConstantName::WEBHOOK:
case CXmlConstantValue::MEDIA_TYPE_WEBHOOK:
diff --git a/ui/include/classes/import/validators/CXmlValidatorGeneral.php b/ui/include/classes/import/validators/CXmlValidatorGeneral.php
index 62817d13064..99c299b6cab 100644
--- a/ui/include/classes/import/validators/CXmlValidatorGeneral.php
+++ b/ui/include/classes/import/validators/CXmlValidatorGeneral.php
@@ -147,11 +147,12 @@ class CXmlValidatorGeneral {
}
switch ($this->format) {
- case 'xml':
+ case CImportReaderFactory::XML:
$is_valid_tag = ($tag === $prefix.($index == 0 ? '' : $index) || $tag === $index);
break;
- case 'json':
+ case CImportReaderFactory::YAML:
+ case CImportReaderFactory::JSON:
$is_valid_tag = ctype_digit(strval($tag));
break;
diff --git a/ui/include/classes/mvc/CRouter.php b/ui/include/classes/mvc/CRouter.php
index 922a32422d7..ad901204013 100644
--- a/ui/include/classes/mvc/CRouter.php
+++ b/ui/include/classes/mvc/CRouter.php
@@ -85,12 +85,12 @@ class CRouter {
'dashboard.widget.rfrate' => ['CControllerDashboardWidgetRfRate', 'layout.json', null],
'dashboard.widget.sanitize' => ['CControllerDashboardWidgetSanitize', 'layout.json', null],
'discovery.view' => ['CControllerDiscoveryView', 'layout.htmlpage', 'monitoring.discovery.view'],
- 'export.hosts.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.mediatypes.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.screens.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.sysmaps.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.templates.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.valuemaps.xml' => ['CControllerExportXml', 'layout.xml', null],
+ 'export.hosts' => ['CControllerExport', 'layout.export', null],
+ 'export.mediatypes' => ['CControllerExport', 'layout.export', null],
+ 'export.screens' => ['CControllerExport', 'layout.export', null],
+ 'export.sysmaps' => ['CControllerExport', 'layout.export', null],
+ 'export.templates' => ['CControllerExport', 'layout.export', null],
+ 'export.valuemaps' => ['CControllerExport', 'layout.export', null],
'favourite.create' => ['CControllerFavouriteCreate', 'layout.javascript', null],
'favourite.delete' => ['CControllerFavouriteDelete', 'layout.javascript', null],
'gui.edit' => ['CControllerGuiEdit', 'layout.htmlpage', 'administration.gui.edit'],
diff --git a/ui/include/classes/setup/CFrontendSetup.php b/ui/include/classes/setup/CFrontendSetup.php
index 4f26b734db0..ca7585525ac 100644
--- a/ui/include/classes/setup/CFrontendSetup.php
+++ b/ui/include/classes/setup/CFrontendSetup.php
@@ -33,6 +33,7 @@ class CFrontendSetup {
const MIN_PHP_MAX_INPUT_TIME = 300;
const MIN_PHP_GD_VERSION = '2.0';
const MIN_PHP_LIBXML_VERSION = '2.6.15';
+ const MIN_PHP_LIBYAML_VERION = '2.0.2'; // See https://pecl.php.net/package/yaml
const REQUIRED_PHP_ARG_SEPARATOR_OUTPUT = '&';
/**
@@ -76,10 +77,12 @@ class CFrontendSetup {
$result[] = $this->checkPhpGdJpeg();
$result[] = $this->checkPhpGdGif();
$result[] = $this->checkPhpGdFreeType();
+ $result[] = $this->checkPhpLibYAML();
$result[] = $this->checkPhpLibxml();
$result[] = $this->checkPhpXmlWriter();
$result[] = $this->checkPhpXmlReader();
$result[] = $this->checkPhpLdapModule();
+ $result[] = $this->checkPhpOpenSsl();
$result[] = $this->checkPhpCtype();
$result[] = $this->checkPhpSession();
$result[] = $this->checkPhpSessionAutoStart();
@@ -461,6 +464,27 @@ class CFrontendSetup {
}
/**
+ * Checks for PHP LibYAML extension.
+ *
+ * @return array
+ */
+ public function checkPhpLibYAML(): array {
+ if (!$current = phpversion('yaml')) {
+ $current = _('unknown');
+ }
+
+ $check = version_compare($current, self::MIN_PHP_LIBYAML_VERION, '>=');
+
+ return [
+ 'name' => _('PHP LibYAML'),
+ 'current' => $current,
+ 'required' => self::MIN_PHP_LIBYAML_VERION,
+ 'result' => $check ? self::CHECK_OK : self::CHECK_FATAL,
+ 'error' => _('PHP LibYAML extension missing.')
+ ];
+ }
+
+ /**
* Checks for PHP libxml extension.
*
* @return array
@@ -535,6 +559,23 @@ class CFrontendSetup {
}
/**
+ * Checks for PHP OpenSSL extension.
+ *
+ * @return array
+ */
+ public function checkPhpOpenSsl() {
+ $current = extension_loaded('openssl');
+
+ return [
+ 'name' => _('PHP OpenSSL'),
+ 'current' => $current ? _('on') : _('off'),
+ 'required' => null,
+ 'result' => $current ? self::CHECK_OK : self::CHECK_WARNING,
+ 'error' => _('PHP OpenSSL extension missing.')
+ ];
+ }
+
+ /**
* Checks for PHP ctype extension.
*
* @return array
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index 20eb72d2d79..a0780555f67 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -1251,16 +1251,6 @@ define('SERVER_CHECK_INTERVAL', 10);
define('DATE_TIME_FORMAT_SECONDS_XML', 'Y-m-d\TH:i:s\Z');
-// XML export|import tags
-define('XML_TAG_MACRO', 'macro');
-define('XML_TAG_HOST', 'host');
-define('XML_TAG_HOSTINVENTORY', 'host_inventory');
-define('XML_TAG_ITEM', 'item');
-define('XML_TAG_TRIGGER', 'trigger');
-define('XML_TAG_GRAPH', 'graph');
-define('XML_TAG_GRAPH_ELEMENT', 'graph_element');
-define('XML_TAG_DEPENDENCY', 'dependency');
-
define('ZBX_DEFAULT_IMPORT_HOST_GROUP', 'Imported hosts');
// XML import flags
@@ -1564,6 +1554,9 @@ define('ZBX_STYLE_ARROW_UP', 'arrow-up');
define('ZBX_STYLE_BLUE', 'blue');
define('ZBX_STYLE_BTN_ADD_FAV', 'btn-add-fav');
define('ZBX_STYLE_BTN_ALT', 'btn-alt');
+define('ZBX_STYLE_BTN_TOGGLE_CHEVRON', 'btn-toggle-chevron');
+define('ZBX_STYLE_BTN_SPLIT', 'btn-split');
+define('ZBX_STYLE_BTN_TOGGLE', 'btn-dropdown-toggle');
define('ZBX_STYLE_BTN_BACK_MAP', 'btn-back-map');
define('ZBX_STYLE_BTN_BACK_MAP_CONTAINER', 'btn-back-map-container');
define('ZBX_STYLE_BTN_BACK_MAP_CONTENT', 'btn-back-map-content');
diff --git a/ui/include/hosts.inc.php b/ui/include/hosts.inc.php
index ed8f83a4cfe..43a103bb9fc 100644
--- a/ui/include/hosts.inc.php
+++ b/ui/include/hosts.inc.php
@@ -533,33 +533,6 @@ function get_host_by_hostid($hostid, $no_error_message = 0) {
return false;
}
-function updateHostStatus($hostids, $status) {
- zbx_value2array($hostids);
-
- $hostIds = [];
- $oldStatus = ($status == HOST_STATUS_MONITORED ? HOST_STATUS_NOT_MONITORED : HOST_STATUS_MONITORED);
-
- $db_hosts = DBselect(
- 'SELECT h.hostid,h.host,h.status'.
- ' FROM hosts h'.
- ' WHERE '.dbConditionInt('h.hostid', $hostids).
- ' AND h.status='.zbx_dbstr($oldStatus)
- );
- while ($host = DBfetch($db_hosts)) {
- $hostIds[] = $host['hostid'];
-
- $host_new = $host;
- $host_new['status'] = $status;
- add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $host['hostid'], $host['host'], 'hosts', $host, $host_new);
- info(_s('Updated status of host "%1$s".', $host['host']));
- }
-
- return DB::update('hosts', [
- 'values' => ['status' => $status],
- 'where' => ['hostid' => $hostIds]
- ]);
-}
-
/**
* Get parent templates for each given application.
*
diff --git a/ui/include/views/configuration.host.discovery.list.php b/ui/include/views/configuration.host.discovery.list.php
index d9a8c7f9d00..c0c2f56bbce 100644
--- a/ui/include/views/configuration.host.discovery.list.php
+++ b/ui/include/views/configuration.host.discovery.list.php
@@ -263,12 +263,14 @@ foreach ($data['discoveries'] as $discovery) {
),
CViewHelper::showNum($discovery['graphs'])
],
- [
- new CLink(_('Host prototypes'),
- (new CUrl('host_prototypes.php'))->setArgument('parent_discoveryid', $discovery['itemid'])
- ),
- CViewHelper::showNum($discovery['hostPrototypes'])
- ],
+ ($discovery['hosts'][0]['flags'] == ZBX_FLAG_DISCOVERY_NORMAL)
+ ? [
+ new CLink(_('Host prototypes'),
+ (new CUrl('host_prototypes.php'))->setArgument('parent_discoveryid', $discovery['itemid'])
+ ),
+ CViewHelper::showNum($discovery['hostPrototypes'])
+ ]
+ : '',
(new CDiv(CHtml::encode($discovery['key_'])))->addClass(ZBX_STYLE_WORDWRAP),
$discovery['delay'],
item_type2str($discovery['type']),
diff --git a/ui/include/views/configuration.host.list.php b/ui/include/views/configuration.host.list.php
index 92fbecbc611..b8df73cc781 100644
--- a/ui/include/views/configuration.host.list.php
+++ b/ui/include/views/configuration.host.list.php
@@ -468,13 +468,12 @@ $form->addItem([
[
'host.massenable' => ['name' => _('Enable'), 'confirm' => _('Enable selected hosts?')],
'host.massdisable' => ['name' => _('Disable'), 'confirm' => _('Disable selected hosts?')],
- 'host.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.hosts.xml')
- ->setArgument('backurl', (new CUrl('hosts.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'host.export' => [
+ 'content' => new CButtonExport('export.hosts',
+ (new CUrl('hosts.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'host.massupdateform' => ['name' => _('Mass update')],
'host.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected hosts?')]
diff --git a/ui/include/views/configuration.template.list.php b/ui/include/views/configuration.template.list.php
index e39bcbab2ae..66ce0680b1f 100644
--- a/ui/include/views/configuration.template.list.php
+++ b/ui/include/views/configuration.template.list.php
@@ -298,13 +298,12 @@ $form->addItem([
$data['paging'],
new CActionButtonList('action', 'templates',
[
- 'template.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.templates.xml')
- ->setArgument('backurl', (new CUrl('templates.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'template.export' => [
+ 'content' => new CButtonExport('export.templates',
+ (new CUrl('templates.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'template.massupdateform' => ['name' => _('Mass update')],
'template.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected templates?')],
diff --git a/ui/include/views/js/conf.import.js.php b/ui/include/views/js/conf.import.js.php
index dd0329ac625..3bd335f6c67 100644
--- a/ui/include/views/js/conf.import.js.php
+++ b/ui/include/views/js/conf.import.js.php
@@ -28,7 +28,7 @@
jQuery(function($) {
$('#import').click(function() {
if ($('.deleteMissing:checked').length > 0) {
- return confirm(<?= json_encode(_('Delete all elements that are not present in the XML file?')) ?>);
+ return confirm(<?= json_encode(_('Delete all elements that are not present in the import file?')) ?>);
}
});
});
diff --git a/ui/include/views/monitoring.screen.list.php b/ui/include/views/monitoring.screen.list.php
index 4d53f7359a6..cd669a4e130 100644
--- a/ui/include/views/monitoring.screen.list.php
+++ b/ui/include/views/monitoring.screen.list.php
@@ -121,13 +121,12 @@ foreach ($data['screens'] as $screen) {
$buttons = [];
if (!$data['templateid']) {
- $buttons['screen.export'] = ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.screens.xml')
- ->setArgument('backurl', (new CUrl('screenconf.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ $buttons['screen.export'] = [
+ 'content' => new CButtonExport('export.screens',
+ (new CUrl('screenconf.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
];
}
diff --git a/ui/include/views/monitoring.sysmap.list.php b/ui/include/views/monitoring.sysmap.list.php
index f77680965ad..30b5b05ed4d 100644
--- a/ui/include/views/monitoring.sysmap.list.php
+++ b/ui/include/views/monitoring.sysmap.list.php
@@ -94,13 +94,12 @@ $sysmapForm->addItem([
$sysmapTable,
$this->data['paging'],
new CActionButtonList('action', 'maps', [
- 'map.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.sysmaps.xml')
- ->setArgument('backurl', (new CUrl('sysmaps.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'map.export' => [
+ 'content' => new CButtonExport('export.sysmaps',
+ (new CUrl('sysmaps.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'map.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected maps?')]
])
diff --git a/ui/js/class.cnavtree.js b/ui/js/class.cnavtree.js
index 5ed44cafa32..c52a3ccfda3 100644
--- a/ui/js/class.cnavtree.js
+++ b/ui/js/class.cnavtree.js
@@ -1267,6 +1267,14 @@ jQuery(function($) {
});
},
+ // onWidgetCopy trigger method
+ onWidgetCopy: function() {
+ var $this = $(this);
+ return this.each(function() {
+ updateWidgetFields($this);
+ });
+ },
+
// onEditStart trigger method
onEditStart: function() {
var $this = $(this);
@@ -1314,7 +1322,9 @@ jQuery(function($) {
lastId: 0
});
- var triggers = ['onEditStart', 'beforeDashboardSave','beforeConfigLoad', 'onDashboardReady'];
+ var triggers = ['onEditStart', 'beforeDashboardSave', 'onWidgetCopy', 'beforeConfigLoad',
+ 'onDashboardReady'
+ ];
$.each(triggers, function(index, trigger) {
$(".dashbrd-grid-container").dashboardGrid("addAction", trigger,
diff --git a/ui/js/dashboard.grid.js b/ui/js/dashboard.grid.js
index 64449797811..469b7d7b6e9 100644
--- a/ui/js/dashboard.grid.js
+++ b/ui/js/dashboard.grid.js
@@ -3853,7 +3853,7 @@
},
/**
- * Function to store copied widget into storage handler.
+ * Function to store copied widget into storage buffer.
*
* @param {object} widget Widget object copied.
*
@@ -3861,6 +3861,11 @@
*/
copyWidget: function(widget) {
return this.each(function() {
+ var $this = $(this),
+ data = $this.data('dashboardGrid');
+
+ doAction('onWidgetCopy', $this, data, widget);
+
var w = {
type: widget.type,
pos: {
diff --git a/ui/js/init.js b/ui/js/init.js
index 5db35d0761b..0a5b2b9020f 100644
--- a/ui/js/init.js
+++ b/ui/js/init.js
@@ -255,8 +255,7 @@ jQuery(function($) {
return {
of: $obj,
my: 'left top',
- at: 'left top+24',
- collision: 'none'
+ at: 'left bottom'
};
case 'submenu':
diff --git a/ui/js/menupopup.js b/ui/js/menupopup.js
index 035b40356e3..28ff96c2446 100644
--- a/ui/js/menupopup.js
+++ b/ui/js/menupopup.js
@@ -918,20 +918,41 @@ function getMenuPopupDropdown(options, trigger_elem) {
var items = [];
jQuery.each(options.items, function(i, item) {
- items.push({
+ var row = {
label: item.label,
- url: item.url || 'javascript:void(0);',
- class: item.class,
- clickCallback: () => {
+ url: item.url || 'javascript:void(0);'
+ };
+
+ if (item.class) {
+ row.class = item.class;
+ }
+
+ if (options.toggle_class) {
+ row.clickCallback = () => {
jQuery(trigger_elem)
.removeClass()
- .addClass(['btn-alt', 'btn-dropdown-toggle', item.class].join(' '));
+ .addClass(['btn-alt', options.toggle_class, item.class].join(' '));
jQuery('input[type=hidden]', jQuery(trigger_elem).parent())
.val(item.value)
.trigger('change');
}
- });
+ }
+ else if (options.submit_form) {
+ row.url = 'javascript:void(0);';
+ row.clickCallback = () => {
+ var $_form = trigger_elem.closest('form');
+
+ if (!$_form.data("action")) {
+ $_form.data("action", $_form.attr("action"));
+ }
+
+ $_form.attr("action", item.url);
+ $_form.submit();
+ }
+ }
+
+ items.push(row);
});
return [{
diff --git a/ui/tests/api_json/data/data_test.sql b/ui/tests/api_json/data/data_test.sql
index 23f14fbf620..162f8e26721 100644
--- a/ui/tests/api_json/data/data_test.sql
+++ b/ui/tests/api_json/data/data_test.sql
@@ -242,7 +242,7 @@ INSERT INTO interface (interfaceid, hostid, main, type, useip, ip, dns, port) VA
INSERT INTO hosts (hostid, host, status, description) VALUES (99003, 'Api active proxy in action', 5, '');
INSERT INTO hosts (hostid, host, status, description) VALUES (99004, 'Api active proxy with host', 5, '');
INSERT INTO hosts (hostid, proxy_hostid, host, name, status, description) VALUES (99005, 99004,'API Host monitored with proxy', 'API Host monitored with proxy', 0, '');
-INSERT INTO interface (interfaceid,hostid,main,type,useip,ip,dns,port) values (99003,99004,1,1,1,'127.0.0.1','','10050');
+INSERT INTO interface (interfaceid,hostid,main,type,useip,ip,dns,port) values (99003,99005,1,1,1,'127.0.0.1','','10050');
INSERT INTO actions (actionid, name, eventsource, evaltype, status, esc_period) VALUES (90, 'API action with proxy', 1, 0, 0, '1h');
INSERT INTO operations (operationid, actionid, operationtype, esc_period, esc_step_from, esc_step_to, evaltype) VALUES (90, 90, 0, 0, 1, 1, 0);
INSERT INTO opmessage (operationid, default_msg, subject, message, mediatypeid) VALUES (90, 0, 'Discovery: {DISCOVERY.DEVICE.STATUS} {DISCOVERY.DEVICE.IPADDRESS}', 'Discovery rule: {DISCOVERY.RULE.NAME}', NULL);
diff --git a/ui/tests/api_json/testConfiguration.php b/ui/tests/api_json/testConfiguration.php
index 615f02a9c0d..31da167fb8e 100644
--- a/ui/tests/api_json/testConfiguration.php
+++ b/ui/tests/api_json/testConfiguration.php
@@ -25,12 +25,13 @@ class testConfiguration extends CAPITest {
public static function export_fail_data() {
return [
+ // Check format parameter.
[
'export' => [
'options' => [
'hosts' => [
'50009'
- ],
+ ]
]
],
'expected_error' => 'Invalid parameter "/": the parameter "format" is missing.'
@@ -40,29 +41,30 @@ class testConfiguration extends CAPITest {
'options' => [
'hosts' => [
'50009'
- ],
+ ]
],
'format' => ''
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'export' => [
'options' => [
'hosts' => [
'50009'
- ],
+ ]
],
- 'format' => 'test'
+ 'format' => 'æų'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
+ // Check unexpected parameter.
[
'export' => [
'options' => [
'groups' => [
'50012'
- ],
+ ]
],
'format' => 'test',
'hosts' => '50009'
@@ -82,16 +84,68 @@ class testConfiguration extends CAPITest {
],
[
'export' => [
+ 'options' => [
+ 'groups' => [
+ '50009'
+ ],
+ 'group' => [
+ '50009'
+ ]
+ ],
+ 'format' => 'xml'
+ ],
+ 'expected_error' => 'Invalid parameter "/options": unexpected parameter "group".'
+ ],
+ // Check missing options parameter.
+ [
+ 'export' => [
'format' => 'xml'
],
'expected_error' => 'Invalid parameter "/": the parameter "options" is missing.'
+ ],
+ // Check prettyprint parameter.
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'yaml',
+ 'prettyprint' => 'test'
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
+ ],
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'json',
+ 'prettyprint' => ''
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
+ ],
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'yaml',
+ 'prettyprint' => 'æų'
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
]
];
}
/**
- * @dataProvider export_fail_data
- */
+ * @dataProvider export_fail_data
+ */
public function testConfiguration_ExportFail($export, $expected_error) {
$this->call('configuration.export', $export, $expected_error);
}
@@ -109,10 +163,10 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider export_string_ids
- */
+ * @dataProvider export_string_ids
+ */
public function testConfiguration_ExportIdsNotNumber($options) {
- $formats = ['xml', 'json'];
+ $formats = ['xml', 'json', 'yaml'];
foreach ($formats as $parameter){
$this->call('configuration.export',
@@ -120,7 +174,7 @@ class testConfiguration extends CAPITest {
'options' => [
$options => [
$options
- ],
+ ]
],
'format' => $parameter
],
@@ -132,40 +186,91 @@ class testConfiguration extends CAPITest {
public static function export_success_data() {
return [
[
- ['groups' => ['50012']]
+ [
+ 'options' => [
+ 'groups' => []
+ ],
+ 'prettyprint' => true
+ ]
+ ],
+ [
+ [
+ 'options' => [
+ 'groups' => ['11111111111111']
+ ],
+ 'prettyprint' => true
+ ]
],
[
- ['hosts' => ['50009']]
+ [
+ 'options' => [
+ 'groups' => ['50012']
+ ],
+ 'prettyprint' => true
+ ]
],
[
- ['images' => ['1']]
+ [
+ 'options' => [
+ 'hosts' => ['50009']
+ ],
+ 'prettyprint' => false
+ ]
+ ],
+ [
+ [
+ 'options' => [
+ 'groups' => ['50012'],
+ 'hosts' => ['50009']
+ ]
+ ]
],
[
- ['maps' => ['1']]
+ [
+ 'options' => [
+ 'images' => ['1']
+ ]
+ ]
],
[
- ['screens' => ['3']]
+ [
+ 'options' => [
+ 'maps' => ['1']
+ ]
+ ]
],
[
- ['templates' => ['10069']]
+ [
+ 'options' => [
+ 'screens' => ['3']
+ ]
+ ]
],
[
- ['valueMaps' => ['1']]
+ [
+ 'options' => [
+ 'templates' => ['10069']
+ ]
+ ]
],
+ [
+ [
+ 'options' => [
+ 'valueMaps' => ['1']
+ ]
+ ]
+ ]
];
}
/**
- * @dataProvider export_success_data
- */
+ * @dataProvider export_success_data
+ */
public function testConfiguration_ExportSuccess($data) {
- $formats = ['xml', 'json'];
+ $formats = ['xml', 'json', 'yaml'];
- foreach ($formats as $parameter){
- $this->call('configuration.export', [
- 'options' => $data,
- 'format' => $parameter
- ]);
+ foreach ($formats as $parameter) {
+ $this->call('configuration.export', array_merge($data, ['format' => $parameter]));
}
}
@@ -213,7 +318,7 @@ class testConfiguration extends CAPITest {
],
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'import' => [
@@ -225,7 +330,7 @@ class testConfiguration extends CAPITest {
],
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'import' => [
@@ -240,6 +345,19 @@ class testConfiguration extends CAPITest {
],
'expected_error' => 'Invalid parameter "/": unexpected parameter "hosts".'
],
+ [
+ 'import' => [
+ 'format' => 'json',
+ 'rules' => [
+ 'groups' => [
+ 'createMissing' => true
+ ]
+ ],
+ 'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}',
+ 'prettyprint' => true
+ ],
+ 'expected_error' => 'Invalid parameter "/": unexpected parameter "prettyprint".'
+ ],
// Check rules.
[
'import' => [
@@ -290,8 +408,8 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider import_fail_data
- */
+ * @dataProvider import_fail_data
+ */
public function testConfiguration_ImportFail($import, $expected_error) {
$this->call('configuration.import', $import, $expected_error);
}
@@ -377,8 +495,8 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider import_rules_parameters
- */
+ * @dataProvider import_rules_parameters
+ */
public function testConfiguration_ImportBooleanTypeAndUnexpectedParameters($import) {
foreach ($import['expected'] as $expected) {
$this->call('configuration.import', [
@@ -413,12 +531,12 @@ class testConfiguration extends CAPITest {
return [
[[
'format' => 'xml',
- 'source' => '' ,
+ 'source' => '',
'error' => 'Cannot read XML: XML is empty.'
]],
[[
'format' => 'xml',
- 'source' => 'test' ,
+ 'source' => 'test',
'error' => 'Cannot read XML: (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]],
[[
@@ -430,7 +548,7 @@ class testConfiguration extends CAPITest {
[[
'format' => 'xml',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
- <zabbix_export><version></version><date>2016-12-09T07:12:45Z</date></zabbix_export>' ,
+ <zabbix_export><version></version><date>2016-12-09T07:12:45Z</date></zabbix_export>',
'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
]],
[[
@@ -440,45 +558,92 @@ class testConfiguration extends CAPITest {
// can be different error message text
'error_contains' => 'Cannot read XML:'
]],
+ // JSON format.
[[
'format' => 'json',
- 'source' => '' ,
+ 'source' => '',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: No error.'
'error_contains' => 'Cannot read JSON: '
]],
[[
'format' => 'json',
- 'source' => 'test' ,
+ 'source' => 'test',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: boolean expected.'
'error_contains' => 'Cannot read JSON: '
]],
[[
'format' => 'json',
- 'source' => '{"zabbix_export":{"date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"zabbix_export":{"date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/zabbix_export": the tag "version" is missing.'
]],
[[
'format' => 'json',
- 'source' => '{"zabbix_export":{"version":"","date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"zabbix_export":{"version":"","date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
]],
[[
'format' => 'json',
- 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/": unexpected tag "export".'
]],
[[
'format' => 'json',
- 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}' ,
+ 'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: unexpected end of data.'
'error_contains' => 'Cannot read JSON: '
- ]]
+ ]],
+ // YAML format.
+ [[
+ 'format' => 'yaml',
+ 'source' => '',
+ 'error_contains' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => 'æų',
+ 'error_contains' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n date: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/zabbix_export": the tag "version" is missing.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n version: \"5.0\"\ndate: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/": unexpected tag "date".'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n version: \"\"\n date: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nexport:\n version: \"4.0\"\n date: \"2020-08-03T11:38:33Z\"\n...\n",
+ 'error' => 'Invalid tag "/": unexpected tag "export".'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T11:38:33Z',
+ 'error_contains' => 'Cannot read YAML: scanning error encountered during parsing'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<zabbix_export><version>5.0</version><date>2020-08-03T12:36:11Z</date></zabbix_export>\n',
+ 'error' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '{\"zabbix_export\":{\"version\":\"5.0\",\"date\":\"2020-08-03T12:36:39Z\"}}',
+ 'error' => 'Cannot read YAML: scanning error encountered during parsing: found unexpected \':\' (line 1, column 19), context while scanning a plain scalar (line 1, column 2).'
+ ]],
];
}
/**
- * @dataProvider import_source
- */
+ * @dataProvider import_source
+ */
public function testConfiguration_ImportInvalidSource($data) {
$result = $this->call('configuration.import', [
'format' => $data['format'],
@@ -525,6 +690,12 @@ class testConfiguration extends CAPITest {
'sql' => 'select * from hstgrp where name=\'API host group json import\''
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'groups',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:41:17Z\"\n groups:\n - name: API host group yaml import\n...\n",
+ 'sql' => 'select * from hstgrp where name=\'API host group yaml import\''
+ ],
+ [
'format' => 'xml',
'parameter' => 'screens',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
@@ -549,6 +720,13 @@ class testConfiguration extends CAPITest {
'sql' => 'select * from screens where name=\'API screen json import\''
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'screens',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:44:35Z\"\n".
+ " screens:\n - name: API screen yaml import\n hsize: \"1\"\n vsize: \"1\"\n screen_items: []\n...\n",
+ 'sql' => 'select * from screens where name=\'API screen yaml import\''
+ ],
+ [
'format' => 'xml',
'parameter' => 'valueMaps',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
@@ -575,6 +753,13 @@ class testConfiguration extends CAPITest {
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-12T07:18:00Z","value_maps":[{"name":"API valueMap json import",'
. '"mappings":[{"value":"1","newvalue":"Up"}]}]}}',
'sql' => 'select * from valuemaps where name=\'API valueMap json import\''
+ ],
+ [
+ 'format' => 'yaml',
+ 'parameter' => 'valueMaps',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:47:05Z\"\n".
+ " value_maps:\n - name: API valueMap yaml import\n mappings:\n - value: One\n newvalue: Up\n...\n",
+ 'sql' => 'select * from valuemaps where name=\'API valueMap yaml import\''
]
];
}
@@ -624,6 +809,13 @@ class testConfiguration extends CAPITest {
'expected_error' => 'Only Super Admins can create host groups.'
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'groups',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:41:17Z\"\n groups:\n - name: API host group yaml import as non Super Admin\n...\n",
+ 'sql' => 'select * from hstgrp where name=\'API host group yaml import as non Super Admin\'',
+ 'expected_error' => 'Only Super Admins can create host groups.'
+ ],
+ [
'format' => 'xml',
'parameter' => 'valueMaps',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
diff --git a/ui/tests/selenium/testFormAdministrationGeneralInstallation.php b/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
index 029c2f357dd..20e9e83d867 100644
--- a/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
+++ b/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
@@ -46,10 +46,13 @@ class testFormAdministrationGeneralInstallation extends CLegacyWebTest {
'PHP gd',
'PHP gd PNG support',
'PHP gd JPEG support',
+ 'PHP gd GIF support',
'PHP gd FreeType support',
+ 'PHP LibYAML',
'PHP libxml',
'PHP xmlwriter',
'PHP xmlreader',
+ 'PHP LDAP',
'PHP ctype',
'PHP session',
'PHP option "session.auto_start"',