zabbix_manager 5.1.6 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -1
- data/README.md +73 -45
- data/lib/zabbix_manager/classes/actions.rb +3 -11
- data/lib/zabbix_manager/classes/applications.rb +2 -8
- data/lib/zabbix_manager/classes/configurations.rb +4 -9
- data/lib/zabbix_manager/classes/{drules.rb → discovery_rules.rb} +2 -8
- data/lib/zabbix_manager/classes/errors.rb +3 -0
- data/lib/zabbix_manager/classes/events.rb +2 -1
- data/lib/zabbix_manager/classes/graphs.rb +19 -15
- data/lib/zabbix_manager/classes/{hostgroups.rb → host_groups.rb} +5 -11
- data/lib/zabbix_manager/classes/host_interfaces.rb +261 -0
- data/lib/zabbix_manager/classes/hosts.rb +139 -89
- data/lib/zabbix_manager/classes/{httptests.rb → http_tests.rb} +2 -8
- data/lib/zabbix_manager/classes/items.rb +139 -92
- data/lib/zabbix_manager/classes/maintenance.rb +2 -1
- data/lib/zabbix_manager/classes/{mediatypes.rb → media_types.rb} +2 -7
- data/lib/zabbix_manager/classes/problems.rb +11 -40
- data/lib/zabbix_manager/classes/proxies.rb +7 -15
- data/lib/zabbix_manager/classes/proxy_groups.rb +16 -0
- data/lib/zabbix_manager/classes/roles.rb +13 -19
- data/lib/zabbix_manager/classes/screens.rb +11 -30
- data/lib/zabbix_manager/classes/scripts.rb +2 -8
- data/lib/zabbix_manager/classes/server.rb +1 -0
- data/lib/zabbix_manager/classes/templates.rb +2 -65
- data/lib/zabbix_manager/classes/triggers.rb +62 -53
- data/lib/zabbix_manager/classes/user_groups.rb +63 -0
- data/lib/zabbix_manager/classes/user_macros.rb +111 -0
- data/lib/zabbix_manager/classes/users.rb +15 -54
- data/lib/zabbix_manager/classes/value_maps.rb +52 -0
- data/lib/zabbix_manager/client.rb +162 -279
- data/lib/zabbix_manager/configuration.rb +115 -0
- data/lib/zabbix_manager/http_transport.rb +145 -97
- data/lib/zabbix_manager/log_sanitizer.rb +18 -29
- data/lib/zabbix_manager/monitoring/device.rb +394 -0
- data/lib/zabbix_manager/monitoring/expressions.rb +36 -0
- data/lib/zabbix_manager/monitoring/line.rb +176 -0
- data/lib/zabbix_manager/monitoring/line_items.rb +110 -0
- data/lib/zabbix_manager/monitoring/line_lifecycle.rb +169 -0
- data/lib/zabbix_manager/monitoring/line_plan.rb +113 -0
- data/lib/zabbix_manager/monitoring/thresholds.rb +182 -0
- data/lib/zabbix_manager/monitoring/traffic_items.rb +133 -0
- data/lib/zabbix_manager/monitoring/validation.rb +92 -0
- data/lib/zabbix_manager/monitoring.rb +216 -642
- data/lib/zabbix_manager/resource.rb +262 -0
- data/lib/zabbix_manager/traffic.rb +239 -0
- data/lib/zabbix_manager/version.rb +1 -1
- data/lib/zabbix_manager.rb +66 -179
- data/zabbix_manager.gemspec +6 -22
- metadata +41 -190
- data/lib/zabbix_manager/basic/basic_alias.rb +0 -42
- data/lib/zabbix_manager/basic/basic_func.rb +0 -55
- data/lib/zabbix_manager/basic/basic_init.rb +0 -50
- data/lib/zabbix_manager/basic/basic_logic.rb +0 -239
- data/lib/zabbix_manager/classes/hostinterfaces.rb +0 -189
- data/lib/zabbix_manager/classes/usergroups.rb +0 -75
- data/lib/zabbix_manager/classes/usermacros.rb +0 -222
- data/lib/zabbix_manager/classes/valuemaps.rb +0 -26
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
|
-
|
|
4
|
+
# 远端脚本执行与可用脚本查询;执行请求可能产生 API 之外的设备副作用。
|
|
5
|
+
class Scripts < Resource
|
|
5
6
|
# 返回 Zabbix API 中脚本对象的方法名前缀。
|
|
6
7
|
#
|
|
7
8
|
# @return [String] 脚本对象的方法名前缀
|
|
@@ -9,13 +10,6 @@ class ZabbixManager
|
|
|
9
10
|
"script"
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
# 返回用于唯一识别脚本的业务字段名。
|
|
13
|
-
#
|
|
14
|
-
# @return [String] 脚本对象的业务标识字段名
|
|
15
|
-
def identify
|
|
16
|
-
"name"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
13
|
# 在指定主机上执行 Zabbix 脚本。
|
|
20
14
|
#
|
|
21
15
|
# 示例:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
|
-
|
|
4
|
+
# 按模板技术名称解析 ID;返回引用供主机或模板关联操作使用。
|
|
5
|
+
class Templates < Resource
|
|
5
6
|
# 返回 Zabbix API 中模板对象的方法名前缀。
|
|
6
7
|
#
|
|
7
8
|
# @return [String] 模板对象的方法名前缀
|
|
@@ -16,17 +17,6 @@ class ZabbixManager
|
|
|
16
17
|
"host"
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
# 删除指定模板并返回首个已删除模板的 ID。
|
|
20
|
-
#
|
|
21
|
-
# @param data [Array] 要删除的 templateid 数组
|
|
22
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
23
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
24
|
-
# @return [Integer, nil] 已删除模板的 ID,无结果时返回 nil
|
|
25
|
-
def delete(data)
|
|
26
|
-
result = @client.api_request(method: "template.delete", params: [data])
|
|
27
|
-
result.empty? ? nil : result["templateids"][0].to_i
|
|
28
|
-
end
|
|
29
|
-
|
|
30
20
|
# 根据查询条件获取模板 ID 列表。
|
|
31
21
|
#
|
|
32
22
|
# @param data [Hash] Zabbix template.get 查询参数
|
|
@@ -39,59 +29,6 @@ class ZabbixManager
|
|
|
39
29
|
end
|
|
40
30
|
end
|
|
41
31
|
|
|
42
|
-
# 批量更新主机与模板的关联关系。
|
|
43
|
-
#
|
|
44
|
-
# @param data [Hash] 包含 hosts_id 和 templates_id 数组
|
|
45
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
46
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
47
|
-
# @return [Boolean] API 是否返回有效结果
|
|
48
|
-
def mass_update(data)
|
|
49
|
-
result = @client.api_request(
|
|
50
|
-
method: "template.massUpdate",
|
|
51
|
-
params: {
|
|
52
|
-
hosts: data[:hosts_id].map { |t| { hostid: t } },
|
|
53
|
-
templates: data[:templates_id].map { |t| { templateid: t } }
|
|
54
|
-
}
|
|
55
|
-
)
|
|
56
|
-
result.empty? ? false : true
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# 批量为主机添加模板关联。
|
|
60
|
-
#
|
|
61
|
-
# @param data [Hash] 包含 hosts_id 和 templates_id 数组
|
|
62
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
63
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
64
|
-
# @return [Boolean] API 是否返回有效结果
|
|
65
|
-
def mass_add(data)
|
|
66
|
-
result = @client.api_request(
|
|
67
|
-
method: "template.massAdd",
|
|
68
|
-
params: {
|
|
69
|
-
hosts: data[:hosts_id].map { |t| { hostid: t } },
|
|
70
|
-
templates: data[:templates_id].map { |t| { templateid: t } }
|
|
71
|
-
}
|
|
72
|
-
)
|
|
73
|
-
result.empty? ? false : true
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# 批量移除主机的模板或主机组关联。
|
|
77
|
-
#
|
|
78
|
-
# @param data [Hash] 包含 hosts_id、templates_id 及可选 group_id
|
|
79
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
80
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
81
|
-
# @return [Boolean] API 是否返回有效结果
|
|
82
|
-
def mass_remove(data)
|
|
83
|
-
result = @client.api_request(
|
|
84
|
-
method: "template.massRemove",
|
|
85
|
-
params: {
|
|
86
|
-
hostids: data[:hosts_id],
|
|
87
|
-
templateids: data[:templates_id],
|
|
88
|
-
groupids: data[:group_id],
|
|
89
|
-
force: 1
|
|
90
|
-
}
|
|
91
|
-
)
|
|
92
|
-
result.empty? ? false : true
|
|
93
|
-
end
|
|
94
|
-
|
|
95
32
|
# 按模板技术名称查询,并返回可直接用于主机或模板更新的引用对象。
|
|
96
33
|
#
|
|
97
34
|
# @param data [String, Array<String>] 一个或多个模板技术名称
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
|
-
|
|
4
|
+
# 触发器与依赖对账;受管身份使用标签,创建结果不明时只回读、不重放。
|
|
5
|
+
class Triggers < Resource
|
|
5
6
|
DEFAULT_UNCERTAIN_WRITE_DELAYS = [0, 0.25, 1, 2].freeze
|
|
7
|
+
MAX_MANAGED_KEY_LENGTH = 200
|
|
6
8
|
# 返回触发器对应的 Zabbix API 模块名。
|
|
7
9
|
#
|
|
8
10
|
# @return [String]
|
|
@@ -19,13 +21,11 @@ class ZabbixManager
|
|
|
19
21
|
|
|
20
22
|
# 按触发器 ID 查询完整对象及关联项和函数。
|
|
21
23
|
#
|
|
22
|
-
# @param data [Hash]
|
|
23
|
-
# @raise [ApiError]
|
|
24
|
-
# @raise [TransportError]
|
|
25
|
-
# @return [Hash]
|
|
24
|
+
# @param data [Hash] 包含目标触发器 ID 字段及其值
|
|
25
|
+
# @raise [ApiError] Zabbix API 明确返回业务错误
|
|
26
|
+
# @raise [TransportError] HTTP 或网络请求失败,可能无法确认远端结果
|
|
27
|
+
# @return [Array<Hash>] 匹配的触发器及其关联对象
|
|
26
28
|
def dump_by_id(data)
|
|
27
|
-
log "[DEBUG] Call dump_by_id with parameters: #{data.inspect}"
|
|
28
|
-
|
|
29
29
|
@client.api_request(
|
|
30
30
|
method: "trigger.get",
|
|
31
31
|
params: {
|
|
@@ -43,7 +43,7 @@ class ZabbixManager
|
|
|
43
43
|
# @return [Array<Hash>]
|
|
44
44
|
def for_host(hostid, output: "extend", select_items: nil, select_functions: nil,
|
|
45
45
|
select_dependencies: nil, select_tags: nil, filter: nil)
|
|
46
|
-
|
|
46
|
+
positive_id_attribute(hostid, "hostid")
|
|
47
47
|
|
|
48
48
|
params = { hostids: hostid, output: output }
|
|
49
49
|
params[:selectItems] = select_items if select_items
|
|
@@ -51,12 +51,13 @@ class ZabbixManager
|
|
|
51
51
|
params[:selectDependencies] = select_dependencies if select_dependencies
|
|
52
52
|
params[:selectTags] = select_tags if select_tags
|
|
53
53
|
params[:filter] = filter if filter.present?
|
|
54
|
-
@client.api_request(method: "trigger.get", params: params)
|
|
54
|
+
response_objects(@client.api_request(method: "trigger.get", params: params))
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# 按主机、描述和可选标签查询唯一触发器。
|
|
58
58
|
# @return [Hash, nil]
|
|
59
59
|
def find_for_host(hostid:, description:, tags: nil)
|
|
60
|
+
positive_id_attribute(hostid, "hostid")
|
|
60
61
|
params = {
|
|
61
62
|
hostids: hostid,
|
|
62
63
|
filter: { description: description },
|
|
@@ -68,23 +69,33 @@ class ZabbixManager
|
|
|
68
69
|
method: "trigger.get",
|
|
69
70
|
params: params
|
|
70
71
|
)
|
|
72
|
+
response_objects(result)
|
|
73
|
+
result.each { |trigger| response_identifier(trigger["triggerid"]) }
|
|
71
74
|
raise Conflict, "multiple triggers match #{description} on host #{hostid}" if result.length > 1
|
|
72
75
|
|
|
73
76
|
result.first
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
# 按管理标签查询唯一触发器。
|
|
80
|
+
# 展开表达式并读取依赖,供不确定写入后的完整期望状态核对。
|
|
81
|
+
# @param hostid [Integer, String] 触发器所属主机
|
|
82
|
+
# @param managed_key [String] 稳定、非秘密的受管身份
|
|
77
83
|
# @return [Hash, nil]
|
|
78
84
|
def find_managed_for_host(hostid:, managed_key:)
|
|
85
|
+
positive_id_attribute(hostid, "hostid")
|
|
79
86
|
result = @client.api_request(
|
|
80
87
|
method: "trigger.get",
|
|
81
88
|
params: {
|
|
82
89
|
hostids: hostid,
|
|
83
90
|
tags: [{ tag: "zabbix_manager_id", value: managed_key, operator: 1 }],
|
|
84
|
-
output:
|
|
91
|
+
output: "extend",
|
|
92
|
+
expandExpression: true,
|
|
93
|
+
selectDependencies: ["triggerid"],
|
|
85
94
|
selectTags: "extend"
|
|
86
95
|
}
|
|
87
96
|
)
|
|
97
|
+
response_objects(result)
|
|
98
|
+
result.each { |trigger| response_identifier(trigger["triggerid"]) }
|
|
88
99
|
raise Conflict, "multiple triggers use managed key #{managed_key}" if result.length > 1
|
|
89
100
|
|
|
90
101
|
result.first
|
|
@@ -96,7 +107,12 @@ class ZabbixManager
|
|
|
96
107
|
id = normalized_ids([triggerid]).first
|
|
97
108
|
params = { triggerids: id, output: "extend" }
|
|
98
109
|
params[:selectDependencies] = select_dependencies if select_dependencies
|
|
99
|
-
result = @client.api_request(method: "trigger.get", params: params)
|
|
110
|
+
result = response_objects(@client.api_request(method: "trigger.get", params: params))
|
|
111
|
+
result.each do |trigger|
|
|
112
|
+
unless response_identifier(trigger["triggerid"]).to_s == id
|
|
113
|
+
raise ProtocolError, "trigger.get returned an unexpected trigger ID"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
100
116
|
raise Conflict, "multiple triggers use triggerid #{id}" if result.length > 1
|
|
101
117
|
|
|
102
118
|
result.first
|
|
@@ -106,7 +122,8 @@ class ZabbixManager
|
|
|
106
122
|
# 可通过 Client 的 upsert_lock 接入跨进程锁。
|
|
107
123
|
# @return [Integer]
|
|
108
124
|
def upsert_for_host(data)
|
|
109
|
-
attributes = data.deep_symbolize_keys
|
|
125
|
+
attributes = data.deep_symbolize_keys.deep_dup
|
|
126
|
+
positive_id_attribute(attributes[:hostid], "hostid")
|
|
110
127
|
lock_key = "trigger:#{attributes[:hostid]}:#{attributes[:managed_key] || attributes[:description]}"
|
|
111
128
|
@client.with_upsert_lock(lock_key) { perform_upsert_for_host(attributes) }
|
|
112
129
|
end
|
|
@@ -114,13 +131,14 @@ class ZabbixManager
|
|
|
114
131
|
# 批量启用或停用触发器。
|
|
115
132
|
# @return [Array<Integer>]
|
|
116
133
|
def set_status(hostid:, triggerids:, enabled:)
|
|
134
|
+
validate_boolean!(enabled, "enabled")
|
|
117
135
|
ids = normalized_ids(triggerids)
|
|
118
136
|
verify_host_ownership!(hostid, ids)
|
|
119
137
|
result = @client.api_request(
|
|
120
138
|
method: "trigger.update",
|
|
121
139
|
params: ids.map { |triggerid| { triggerid: triggerid, status: enabled ? 0 : 1 } }
|
|
122
140
|
)
|
|
123
|
-
|
|
141
|
+
response_ids(result, expected: ids)
|
|
124
142
|
end
|
|
125
143
|
|
|
126
144
|
# 批量删除明确指定的触发器。
|
|
@@ -129,13 +147,14 @@ class ZabbixManager
|
|
|
129
147
|
ids = normalized_ids(triggerids)
|
|
130
148
|
verify_host_ownership!(hostid, ids)
|
|
131
149
|
result = @client.api_request(method: "trigger.delete", params: ids)
|
|
132
|
-
|
|
150
|
+
response_ids(result, expected: ids)
|
|
133
151
|
end
|
|
134
152
|
|
|
135
153
|
# 使用 trigger.update 完整替换一个触发器的依赖集合。
|
|
136
154
|
# 传入空集合可清除依赖,返回被更新的触发器 ID。
|
|
137
155
|
# @return [Integer]
|
|
138
156
|
def replace_dependencies(hostid:, triggerid:, depends_on:, allow_cross_host_dependencies: false)
|
|
157
|
+
validate_boolean!(allow_cross_host_dependencies, "allow_cross_host_dependencies")
|
|
139
158
|
id = normalized_ids([triggerid]).first
|
|
140
159
|
dependency_ids = normalized_ids(depends_on, allow_empty: true)
|
|
141
160
|
dependencies = dependency_ids.map { |value| { triggerid: value } }
|
|
@@ -143,13 +162,14 @@ class ZabbixManager
|
|
|
143
162
|
|
|
144
163
|
verify_host_ownership!(hostid, [id])
|
|
145
164
|
verify_host_ownership!(hostid, dependency_ids) if dependency_ids.any? && !allow_cross_host_dependencies
|
|
146
|
-
update_dependencies(id, dependencies)
|
|
165
|
+
@client.with_upsert_lock("trigger-dependencies:#{id}") { update_dependencies(id, dependencies) }
|
|
147
166
|
end
|
|
148
167
|
|
|
149
168
|
# 在保留现有依赖的前提下,为触发器追加一个或多个依赖。
|
|
150
169
|
# 读改写过程按触发器 ID 加锁,跨进程可通过 Client 的 upsert_lock 协调。
|
|
151
170
|
# @return [Integer]
|
|
152
171
|
def add_dependencies(hostid:, triggerid:, depends_on:, allow_cross_host_dependencies: false)
|
|
172
|
+
validate_boolean!(allow_cross_host_dependencies, "allow_cross_host_dependencies")
|
|
153
173
|
id = normalized_ids([triggerid]).first
|
|
154
174
|
additions = normalized_ids(depends_on)
|
|
155
175
|
raise Invalid, "trigger cannot depend on itself" if additions.include?(id)
|
|
@@ -177,26 +197,18 @@ class ZabbixManager
|
|
|
177
197
|
attributes = data.deep_symbolize_keys
|
|
178
198
|
hostid = attributes.delete(:hostid)
|
|
179
199
|
managed_key = attributes.delete(:managed_key)&.to_s
|
|
180
|
-
|
|
181
|
-
raise Invalid, "hostid is required" if hostid.blank?
|
|
200
|
+
positive_id_attribute(hostid, "hostid")
|
|
182
201
|
raise Invalid, "description is required" if attributes[:description].blank?
|
|
183
202
|
raise Invalid, "expression is required" if attributes[:expression].blank?
|
|
184
203
|
|
|
185
204
|
if managed_key.present?
|
|
186
|
-
raise Invalid, "managed_key is too long" if managed_key.length >
|
|
205
|
+
raise Invalid, "managed_key is too long" if managed_key.length > MAX_MANAGED_KEY_LENGTH
|
|
187
206
|
|
|
188
207
|
attributes[:tags] = Array(attributes[:tags]).reject do |tag|
|
|
189
208
|
tag[:tag].to_s == "zabbix_manager_id" || tag["tag"].to_s == "zabbix_manager_id"
|
|
190
209
|
end
|
|
191
210
|
attributes[:tags] << { tag: "zabbix_manager_id", value: managed_key }
|
|
192
211
|
current = find_managed_for_host(hostid: hostid, managed_key: managed_key)
|
|
193
|
-
if current.nil? && legacy_identity_tags.present?
|
|
194
|
-
current = find_for_host(
|
|
195
|
-
hostid: hostid,
|
|
196
|
-
description: attributes[:description],
|
|
197
|
-
tags: legacy_identity_tags
|
|
198
|
-
)
|
|
199
|
-
end
|
|
200
212
|
else
|
|
201
213
|
ownership_tags = Array(attributes[:tags]).select do |tag|
|
|
202
214
|
tag[:tag].to_s == "managed_by" || tag["tag"].to_s == "managed_by"
|
|
@@ -204,50 +216,57 @@ class ZabbixManager
|
|
|
204
216
|
current = find_for_host(hostid: hostid, description: attributes[:description], tags: ownership_tags)
|
|
205
217
|
end
|
|
206
218
|
if current
|
|
207
|
-
triggerid = current
|
|
219
|
+
triggerid = current["triggerid"]
|
|
220
|
+
response_identifier(triggerid)
|
|
208
221
|
attributes[:tags] = merge_tags(current["tags"], attributes[:tags]) if attributes[:tags]
|
|
209
|
-
@client.api_request(method: "trigger.update", params: attributes.merge(triggerid: triggerid))
|
|
210
|
-
triggerid
|
|
222
|
+
result = @client.api_request(method: "trigger.update", params: attributes.merge(triggerid: triggerid))
|
|
223
|
+
response_id(result, expected: [triggerid])
|
|
211
224
|
else
|
|
212
225
|
create_managed_trigger(hostid, managed_key, attributes)
|
|
213
226
|
end
|
|
214
227
|
end
|
|
215
228
|
|
|
216
|
-
#
|
|
229
|
+
# 创建触发器;响应丢失时按管理键回读并核对期望状态。
|
|
217
230
|
# @return [Integer]
|
|
218
231
|
# @api private
|
|
219
232
|
private def create_managed_trigger(hostid, managed_key, attributes)
|
|
220
233
|
result = @client.api_request(method: "trigger.create", params: attributes)
|
|
221
|
-
result
|
|
234
|
+
response_id(result)
|
|
222
235
|
rescue TransportError => original_error
|
|
223
|
-
|
|
224
|
-
|
|
236
|
+
if managed_key.present?
|
|
237
|
+
current = recover_created_trigger(hostid: hostid, managed_key: managed_key, attributes: attributes)
|
|
238
|
+
end
|
|
239
|
+
return response_identifier(current["triggerid"]) if current
|
|
225
240
|
|
|
226
241
|
raise ResultUnknown,
|
|
227
242
|
"trigger create result is unknown; inspect managed key #{managed_key.inspect} before retrying: " \
|
|
228
|
-
|
|
229
|
-
rescue ApiError => original_error
|
|
230
|
-
current = recover_created_trigger(hostid: hostid, managed_key: managed_key) if managed_key.present?
|
|
231
|
-
return current.fetch("triggerid").to_i if current
|
|
232
|
-
|
|
233
|
-
raise original_error
|
|
243
|
+
"#{original_error.message}"
|
|
234
244
|
end
|
|
235
245
|
|
|
236
246
|
# 对结果不确定的创建执行短暂退避回读,不重放写请求。
|
|
237
247
|
# @return [Hash, nil]
|
|
238
248
|
# @api private
|
|
239
|
-
private def recover_created_trigger(hostid:, managed_key:)
|
|
249
|
+
private def recover_created_trigger(hostid:, managed_key:, attributes:)
|
|
240
250
|
delays = @client.options.fetch(:uncertain_write_delays, DEFAULT_UNCERTAIN_WRITE_DELAYS)
|
|
241
251
|
delays.each do |delay|
|
|
242
252
|
sleep(delay) if delay.positive?
|
|
243
253
|
current = find_managed_for_host(hostid: hostid, managed_key: managed_key)
|
|
244
|
-
|
|
254
|
+
if current && recovered_trigger_matches?(current, attributes)
|
|
255
|
+
response_identifier(current["triggerid"])
|
|
256
|
+
return current
|
|
257
|
+
end
|
|
245
258
|
rescue ApiError, TransportError
|
|
246
259
|
next
|
|
247
260
|
end
|
|
248
261
|
nil
|
|
249
262
|
end
|
|
250
263
|
|
|
264
|
+
private def recovered_trigger_matches?(current, attributes)
|
|
265
|
+
desired_tags = Array(attributes[:tags]).map(&:deep_symbolize_keys)
|
|
266
|
+
actual_tags = Array(current["tags"]).map(&:deep_symbolize_keys)
|
|
267
|
+
attributes_match?(current, attributes.except(:tags)) && (desired_tags - actual_tags).empty?
|
|
268
|
+
end
|
|
269
|
+
|
|
251
270
|
# 合并受管标签,同时保留调用方未覆盖的运维标签。
|
|
252
271
|
# @return [Array<Hash>]
|
|
253
272
|
# @api private
|
|
@@ -260,24 +279,14 @@ class ZabbixManager
|
|
|
260
279
|
retained + desired
|
|
261
280
|
end
|
|
262
281
|
|
|
263
|
-
# 统一触发器 ID 并拒绝空集合。
|
|
264
|
-
# @return [Array<String>]
|
|
265
|
-
# @api private
|
|
266
|
-
private def normalized_ids(values, allow_empty: false)
|
|
267
|
-
ids = Array(values).filter_map { |value| value.to_s.strip.presence }.uniq
|
|
268
|
-
raise Invalid, "triggerids are required" if ids.empty? && !allow_empty
|
|
269
|
-
|
|
270
|
-
ids
|
|
271
|
-
end
|
|
272
|
-
|
|
273
282
|
# 回查触发器归属,阻止共享高权限令牌跨主机修改。
|
|
274
283
|
private def verify_host_ownership!(hostid, ids)
|
|
275
|
-
|
|
284
|
+
positive_id_attribute(hostid, "hostid")
|
|
276
285
|
|
|
277
286
|
result = @client.api_request(
|
|
278
287
|
method: "trigger.get", params: { hostids: hostid, triggerids: ids, output: ["triggerid"] }
|
|
279
288
|
)
|
|
280
|
-
owned_ids = result.map { |trigger| trigger
|
|
289
|
+
owned_ids = response_objects(result).map { |trigger| response_identifier(trigger["triggerid"]).to_s }
|
|
281
290
|
foreign_ids = ids - owned_ids
|
|
282
291
|
raise Conflict, "triggers do not belong to host #{hostid}: #{foreign_ids.join(", ")}" if foreign_ids.any?
|
|
283
292
|
end
|
|
@@ -287,7 +296,7 @@ class ZabbixManager
|
|
|
287
296
|
result = @client.api_request(
|
|
288
297
|
method: "trigger.update", params: { triggerid: triggerid, dependencies: dependencies }
|
|
289
298
|
)
|
|
290
|
-
result
|
|
299
|
+
response_id(result, expected: [triggerid])
|
|
291
300
|
end
|
|
292
301
|
end
|
|
293
302
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class ZabbixManager
|
|
4
|
+
# 用户组成员和主机组权限替换;空数组表示清空,省略字段不参与更新。
|
|
5
|
+
class UserGroups < Resource
|
|
6
|
+
# @return [String] 原生用户组 API 前缀
|
|
7
|
+
def method_name
|
|
8
|
+
"usergroup"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String] 用户组 ID 字段
|
|
12
|
+
def key
|
|
13
|
+
"usrgrpid"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 完整替换主机组权限;空数组明确清空,Zabbix 6.2 起保留独立的模板组权限。
|
|
17
|
+
# @param user_group_id [Integer, String] 目标用户组 ID
|
|
18
|
+
# @param host_group_ids [Array<Integer, String>] 完整期望主机组集合
|
|
19
|
+
# @param permission [Integer, String] 0 拒绝、2 只读、3 读写
|
|
20
|
+
# @return [Integer] 回执确认的用户组 ID
|
|
21
|
+
def replace_host_group_permissions(user_group_id:, host_group_ids:, permission: 2)
|
|
22
|
+
group_id = normalized_ids([user_group_id]).first
|
|
23
|
+
host_ids = collection_ids(host_group_ids, "host_group_ids", allow_empty: true)
|
|
24
|
+
access = integer_attribute(permission, "permission")
|
|
25
|
+
raise Invalid, "permission must be 0, 2, or 3" unless [0, 2, 3].include?(access)
|
|
26
|
+
|
|
27
|
+
rights_field = api_version >= Gem::Version.new("6.2") ? :hostgroup_rights : :rights
|
|
28
|
+
result = @client.api_request(
|
|
29
|
+
method: "usergroup.update",
|
|
30
|
+
params: { usrgrpid: group_id, rights_field => host_ids.map { |id| { id: id, permission: access } } }
|
|
31
|
+
)
|
|
32
|
+
response_id(result, expected: [group_id])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# 完整替换各用户组成员;空 user_ids 明确移除现有成员。
|
|
36
|
+
# @param user_group_ids [Array<Integer, String>] 待更新用户组 ID
|
|
37
|
+
# @param user_ids [Array<Integer, String>] 每组的完整期望用户集合
|
|
38
|
+
# @return [Array<Integer>] 回执确认的全部用户组 ID
|
|
39
|
+
def replace_users(user_group_ids:, user_ids:)
|
|
40
|
+
group_ids = collection_ids(user_group_ids, "user_group_ids")
|
|
41
|
+
users = collection_ids(user_ids, "user_ids", allow_empty: true)
|
|
42
|
+
membership = api_version >= Gem::Version.new("6.0") ?
|
|
43
|
+
{ users: users.map { |id| { userid: id } } } : { userids: users }
|
|
44
|
+
result = @client.api_request(
|
|
45
|
+
method: "usergroup.update",
|
|
46
|
+
params: group_ids.map { |id| membership.merge(usrgrpid: id) }
|
|
47
|
+
)
|
|
48
|
+
response_ids(result, expected: group_ids)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def api_version
|
|
54
|
+
Gem::Version.new(@client.api_version)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def collection_ids(values, name, allow_empty: false)
|
|
58
|
+
raise Invalid, "#{name} must be an Array" unless values.is_a?(Array)
|
|
59
|
+
|
|
60
|
+
normalized_ids(values, allow_empty: allow_empty)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class ZabbixManager
|
|
4
|
+
# 主机宏由 hostid + macro 唯一定位,全局宏由 macro 唯一定位。
|
|
5
|
+
class UserMacros < Resource
|
|
6
|
+
# @return [String] 宏业务标识
|
|
7
|
+
def identify
|
|
8
|
+
"macro"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [String] API 模块名
|
|
12
|
+
def method_name
|
|
13
|
+
"usermacro"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [String] 主机宏 ID 字段
|
|
17
|
+
def key
|
|
18
|
+
"hostmacroid"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Hash] 主机宏复合身份
|
|
22
|
+
def identity_filter(data)
|
|
23
|
+
attributes = data.deep_symbolize_keys
|
|
24
|
+
{ macro: attributes.fetch(:macro), hostid: attributes.fetch(:hostid) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Integer, nil] 唯一主机宏 ID
|
|
28
|
+
def get_id(data)
|
|
29
|
+
attributes = identity_filter(data)
|
|
30
|
+
unique_macro_id(get_full_data(attributes), "hostmacroid")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Integer, nil] 唯一全局宏 ID
|
|
34
|
+
def get_id_global(data)
|
|
35
|
+
attributes = data.deep_symbolize_keys
|
|
36
|
+
unique_macro_id(get_full_data_global(macro: attributes.fetch(:macro)), "globalmacroid")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Array<Hash>] 主机宏查询结果
|
|
40
|
+
def get_full_data(data)
|
|
41
|
+
attributes = data.deep_symbolize_keys
|
|
42
|
+
params = { filter: attributes.except(:hostid) }
|
|
43
|
+
params[:hostids] = attributes[:hostid] if attributes.key?(:hostid)
|
|
44
|
+
get_raw(params)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Array<Hash>] 全局宏查询结果
|
|
48
|
+
def get_full_data_global(data)
|
|
49
|
+
get_raw(globalmacro: true, filter: data.deep_symbolize_keys)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Integer, nil] 新建主机宏 ID
|
|
53
|
+
def create(data)
|
|
54
|
+
write_macro("create", data, "hostmacroids")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [Integer, nil] 新建全局宏 ID
|
|
58
|
+
def create_global(data)
|
|
59
|
+
write_macro("createglobal", data, "globalmacroids")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Integer, nil] 删除主机宏 ID
|
|
63
|
+
def delete(ids)
|
|
64
|
+
requested = normalized_ids(ids)
|
|
65
|
+
write_macro("delete", requested, "hostmacroids", expected: requested)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [Integer, nil] 删除全局宏 ID
|
|
69
|
+
def delete_global(ids)
|
|
70
|
+
requested = normalized_ids(ids)
|
|
71
|
+
write_macro("deleteglobal", requested, "globalmacroids", expected: requested)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [Integer, nil] 更新主机宏 ID
|
|
75
|
+
def update(data)
|
|
76
|
+
write_macro("update", data, "hostmacroids", expected: [data.with_indifferent_access[:hostmacroid]])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Integer, nil] 更新全局宏 ID
|
|
80
|
+
def update_global(data)
|
|
81
|
+
write_macro("updateglobal", data, "globalmacroids", expected: [data.with_indifferent_access[:globalmacroid]])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @return [Integer, nil] 已存在或新建全局宏 ID
|
|
85
|
+
def get_or_create_global(data)
|
|
86
|
+
get_id_global(data) || create_global(data)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @return [Integer, nil] 新建或更新全局宏 ID
|
|
90
|
+
def create_or_update_global(data)
|
|
91
|
+
attributes = data.deep_symbolize_keys
|
|
92
|
+
id = get_id_global(attributes)
|
|
93
|
+
id ? update_global(attributes.merge(globalmacroid: id)) : create_global(attributes)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def unique_macro_id(result, id_field)
|
|
99
|
+
response_objects(result)
|
|
100
|
+
raise Conflict, "multiple macros match the requested identity" if result.length > 1
|
|
101
|
+
|
|
102
|
+
response_identifier(result.first[id_field], id_field) if result.first
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def write_macro(operation, params, id_field, expected: nil)
|
|
106
|
+
normalized_ids(expected) if expected
|
|
107
|
+
result = @client.api_request(method: "usermacro.#{operation}", params: params)
|
|
108
|
+
expected ? response_ids(result, id_field, expected: expected).first : response_id(result, id_field)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -1,71 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
# @return [String] 用户对象的方法名前缀
|
|
4
|
+
# 用户资源与媒介配置替换;字段名称按服务器版本转换。
|
|
5
|
+
class Users < Resource
|
|
6
|
+
# @return [String] API 模块名
|
|
8
7
|
def method_name
|
|
9
8
|
"user"
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# @return [String] 用户 ID 复数字段名
|
|
15
|
-
def keys
|
|
16
|
-
"userids"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# 返回用户对象的主键字段名。
|
|
20
|
-
#
|
|
21
|
-
# @return [String] 用户对象的主键字段名
|
|
22
|
-
def key
|
|
23
|
-
"userid"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# 返回用于唯一识别用户的业务字段名。
|
|
27
|
-
#
|
|
28
|
-
# @return [String] 用户对象的业务标识字段名
|
|
11
|
+
# @return [String] 用户名字段
|
|
29
12
|
def identify
|
|
30
|
-
"alias"
|
|
13
|
+
Gem::Version.new(@client.api_version) >= Gem::Version.new("5.4") ? "username" : "alias"
|
|
31
14
|
end
|
|
32
15
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
16
|
+
# 替换指定用户的完整媒介配置。
|
|
35
17
|
# @param data [Hash] 包含 userids 和 media
|
|
36
|
-
# @
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
# @return [Integer, nil] 首个更新用户的 ID
|
|
19
|
+
def update_medias(data)
|
|
20
|
+
attributes = data.deep_symbolize_keys
|
|
21
|
+
ids = normalized_ids(attributes.fetch(:userids))
|
|
22
|
+
media_field = Gem::Version.new(@client.api_version) >= Gem::Version.new("5.4") ? :medias : :user_medias
|
|
39
23
|
result = @client.api_request(
|
|
40
|
-
method: "user
|
|
41
|
-
params:
|
|
42
|
-
{
|
|
43
|
-
userid: t,
|
|
44
|
-
user_medias: data[:media]
|
|
45
|
-
}
|
|
24
|
+
method: "user.update",
|
|
25
|
+
params: attributes.fetch(:userids).map do |userid|
|
|
26
|
+
{ userid: userid, media_field => attributes.fetch(:media) }
|
|
46
27
|
end
|
|
47
28
|
)
|
|
48
|
-
result
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# 为指定用户添加媒介配置,当前通过 user.update 完成。
|
|
52
|
-
#
|
|
53
|
-
# @param data [Hash] 包含 userids 和 media
|
|
54
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
55
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
56
|
-
# @return [Integer, nil] 首个已更新用户的 ID
|
|
57
|
-
def add_medias(data)
|
|
58
|
-
medias_helper(data, "update")
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# 批量替换指定用户的媒介配置。
|
|
62
|
-
#
|
|
63
|
-
# @param data [Hash] 包含 userids 和 media
|
|
64
|
-
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
65
|
-
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
66
|
-
# @return [Integer, nil] 首个已更新用户的 ID
|
|
67
|
-
def update_medias(data)
|
|
68
|
-
medias_helper(data, "update")
|
|
29
|
+
response_ids(result, expected: ids).first
|
|
69
30
|
end
|
|
70
31
|
end
|
|
71
32
|
end
|