zabbix_manager 5.1.4 → 5.1.6
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 +25 -9
- data/LICENSE +1 -1
- data/README.md +202 -8
- data/lib/zabbix_manager/basic/basic_alias.rb +25 -3
- data/lib/zabbix_manager/basic/basic_func.rb +32 -61
- data/lib/zabbix_manager/basic/basic_init.rb +28 -13
- data/lib/zabbix_manager/basic/basic_logic.rb +172 -128
- data/lib/zabbix_manager/classes/actions.rb +21 -11
- data/lib/zabbix_manager/classes/applications.rb +12 -17
- data/lib/zabbix_manager/classes/configurations.rb +20 -5
- data/lib/zabbix_manager/classes/drules.rb +11 -23
- data/lib/zabbix_manager/classes/errors.rb +16 -28
- data/lib/zabbix_manager/classes/events.rb +6 -2
- data/lib/zabbix_manager/classes/graphs.rb +41 -34
- data/lib/zabbix_manager/classes/hostgroups.rb +53 -3
- data/lib/zabbix_manager/classes/hostinterfaces.rb +172 -12
- data/lib/zabbix_manager/classes/hosts.rb +197 -68
- data/lib/zabbix_manager/classes/httptests.rb +18 -20
- data/lib/zabbix_manager/classes/items.rb +199 -72
- data/lib/zabbix_manager/classes/maintenance.rb +6 -2
- data/lib/zabbix_manager/classes/mediatypes.rb +7 -63
- data/lib/zabbix_manager/classes/problems.rb +75 -57
- data/lib/zabbix_manager/classes/proxies.rb +48 -7
- data/lib/zabbix_manager/classes/roles.rb +47 -36
- data/lib/zabbix_manager/classes/screens.rb +45 -14
- data/lib/zabbix_manager/classes/scripts.rb +17 -7
- data/lib/zabbix_manager/classes/server.rb +7 -1
- data/lib/zabbix_manager/classes/templates.rb +59 -21
- data/lib/zabbix_manager/classes/triggers.rb +253 -66
- data/lib/zabbix_manager/classes/usergroups.rb +28 -6
- data/lib/zabbix_manager/classes/usermacros.rb +119 -33
- data/lib/zabbix_manager/classes/users.rb +39 -7
- data/lib/zabbix_manager/classes/valuemaps.rb +13 -19
- data/lib/zabbix_manager/client.rb +311 -135
- data/lib/zabbix_manager/http_transport.rb +192 -0
- data/lib/zabbix_manager/log_sanitizer.rb +68 -0
- data/lib/zabbix_manager/monitoring.rb +691 -0
- data/lib/zabbix_manager/version.rb +1 -1
- data/lib/zabbix_manager.rb +118 -40
- data/zabbix_manager.gemspec +21 -8
- metadata +208 -22
- data/lib/zabbix_manager/classes/unusable.rb +0 -11
|
@@ -2,74 +2,112 @@
|
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
4
|
class Templates < Basic
|
|
5
|
-
# Zabbix API
|
|
5
|
+
# 返回 Zabbix API 中模板对象的方法名前缀。
|
|
6
|
+
#
|
|
7
|
+
# @return [String] 模板对象的方法名前缀
|
|
6
8
|
def method_name
|
|
7
9
|
"template"
|
|
8
10
|
end
|
|
9
11
|
|
|
10
|
-
#
|
|
12
|
+
# 返回用于唯一识别模板的业务字段名。
|
|
13
|
+
#
|
|
14
|
+
# @return [String] 模板对象的业务标识字段名
|
|
11
15
|
def identify
|
|
12
16
|
"host"
|
|
13
17
|
end
|
|
14
18
|
|
|
15
|
-
#
|
|
19
|
+
# 删除指定模板并返回首个已删除模板的 ID。
|
|
20
|
+
#
|
|
21
|
+
# @param data [Array] 要删除的 templateid 数组
|
|
22
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
23
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
24
|
+
# @return [Integer, nil] 已删除模板的 ID,无结果时返回 nil
|
|
16
25
|
def delete(data)
|
|
17
26
|
result = @client.api_request(method: "template.delete", params: [data])
|
|
18
27
|
result.empty? ? nil : result["templateids"][0].to_i
|
|
19
28
|
end
|
|
20
29
|
|
|
21
|
-
#
|
|
30
|
+
# 根据查询条件获取模板 ID 列表。
|
|
31
|
+
#
|
|
32
|
+
# @param data [Hash] Zabbix template.get 查询参数
|
|
33
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
34
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
35
|
+
# @return [Array<String>] 模板 ID 数组
|
|
22
36
|
def get_ids_by_host(data)
|
|
23
37
|
@client.api_request(method: "template.get", params: data).map do |tmpl|
|
|
24
38
|
tmpl["templateid"]
|
|
25
39
|
end
|
|
26
40
|
end
|
|
27
41
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# end
|
|
35
|
-
# end
|
|
36
|
-
|
|
37
|
-
# 通过 Zabbix API 批量更新主机的模板
|
|
42
|
+
# 批量更新主机与模板的关联关系。
|
|
43
|
+
#
|
|
44
|
+
# @param data [Hash] 包含 hosts_id 和 templates_id 数组
|
|
45
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
46
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
47
|
+
# @return [Boolean] API 是否返回有效结果
|
|
38
48
|
def mass_update(data)
|
|
39
49
|
result = @client.api_request(
|
|
40
50
|
method: "template.massUpdate",
|
|
41
51
|
params: {
|
|
42
|
-
hosts:
|
|
52
|
+
hosts: data[:hosts_id].map { |t| { hostid: t } },
|
|
43
53
|
templates: data[:templates_id].map { |t| { templateid: t } }
|
|
44
54
|
}
|
|
45
55
|
)
|
|
46
56
|
result.empty? ? false : true
|
|
47
57
|
end
|
|
48
58
|
|
|
49
|
-
#
|
|
59
|
+
# 批量为主机添加模板关联。
|
|
60
|
+
#
|
|
61
|
+
# @param data [Hash] 包含 hosts_id 和 templates_id 数组
|
|
62
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
63
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
64
|
+
# @return [Boolean] API 是否返回有效结果
|
|
50
65
|
def mass_add(data)
|
|
51
66
|
result = @client.api_request(
|
|
52
67
|
method: "template.massAdd",
|
|
53
68
|
params: {
|
|
54
|
-
hosts:
|
|
69
|
+
hosts: data[:hosts_id].map { |t| { hostid: t } },
|
|
55
70
|
templates: data[:templates_id].map { |t| { templateid: t } }
|
|
56
71
|
}
|
|
57
72
|
)
|
|
58
73
|
result.empty? ? false : true
|
|
59
74
|
end
|
|
60
75
|
|
|
61
|
-
#
|
|
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 是否返回有效结果
|
|
62
82
|
def mass_remove(data)
|
|
63
83
|
result = @client.api_request(
|
|
64
84
|
method: "template.massRemove",
|
|
65
85
|
params: {
|
|
66
|
-
hostids:
|
|
86
|
+
hostids: data[:hosts_id],
|
|
67
87
|
templateids: data[:templates_id],
|
|
68
|
-
groupids:
|
|
69
|
-
force:
|
|
88
|
+
groupids: data[:group_id],
|
|
89
|
+
force: 1
|
|
70
90
|
}
|
|
71
91
|
)
|
|
72
92
|
result.empty? ? false : true
|
|
73
93
|
end
|
|
94
|
+
|
|
95
|
+
# 按模板技术名称查询,并返回可直接用于主机或模板更新的引用对象。
|
|
96
|
+
#
|
|
97
|
+
# @param data [String, Array<String>] 一个或多个模板技术名称
|
|
98
|
+
# @return [Array<Hash>, nil] templateid 引用数组,未找到时返回 nil
|
|
99
|
+
def get_template_ids(data)
|
|
100
|
+
result = @client.api_request(
|
|
101
|
+
method: "template.get",
|
|
102
|
+
params: {
|
|
103
|
+
output: "extend",
|
|
104
|
+
filter: {
|
|
105
|
+
host: Array(data)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
).map { |template| { templateid: template.fetch("templateid") } }
|
|
109
|
+
|
|
110
|
+
result.presence
|
|
111
|
+
end
|
|
74
112
|
end
|
|
75
113
|
end
|
|
@@ -2,105 +2,292 @@
|
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
4
|
class Triggers < Basic
|
|
5
|
-
|
|
5
|
+
DEFAULT_UNCERTAIN_WRITE_DELAYS = [0, 0.25, 1, 2].freeze
|
|
6
|
+
# 返回触发器对应的 Zabbix API 模块名。
|
|
7
|
+
#
|
|
8
|
+
# @return [String]
|
|
6
9
|
def method_name
|
|
7
10
|
"trigger"
|
|
8
11
|
end
|
|
9
12
|
|
|
10
|
-
#
|
|
13
|
+
# 使用描述作为通用 CRUD 的业务标识。
|
|
14
|
+
#
|
|
15
|
+
# @return [String]
|
|
11
16
|
def identify
|
|
12
17
|
"description"
|
|
13
18
|
end
|
|
14
19
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
# 按触发器 ID 查询完整对象及关联项和函数。
|
|
21
|
+
#
|
|
22
|
+
# @param data [Hash] Should include desired object's key and value
|
|
23
|
+
# @raise [ApiError] Error returned when there is a problem with the Zabbix API call.
|
|
24
|
+
# @raise [TransportError] Error raised when HTTP status from Zabbix Server response is not a 200 OK.
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def dump_by_id(data)
|
|
27
|
+
log "[DEBUG] Call dump_by_id with parameters: #{data.inspect}"
|
|
28
|
+
|
|
29
|
+
@client.api_request(
|
|
30
|
+
method: "trigger.get",
|
|
31
|
+
params: {
|
|
32
|
+
filter: {
|
|
33
|
+
key.to_sym => data[key.to_sym]
|
|
34
|
+
},
|
|
35
|
+
output: "extend",
|
|
36
|
+
selectItems: "extend",
|
|
37
|
+
selectFunctions: "extend"
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# 查询主机触发器,并按需返回监控项、函数、依赖和标签。
|
|
43
|
+
# @return [Array<Hash>]
|
|
44
|
+
def for_host(hostid, output: "extend", select_items: nil, select_functions: nil,
|
|
45
|
+
select_dependencies: nil, select_tags: nil, filter: nil)
|
|
46
|
+
raise Invalid, "hostid is required" if hostid.blank?
|
|
47
|
+
|
|
48
|
+
params = { hostids: hostid, output: output }
|
|
49
|
+
params[:selectItems] = select_items if select_items
|
|
50
|
+
params[:selectFunctions] = select_functions if select_functions
|
|
51
|
+
params[:selectDependencies] = select_dependencies if select_dependencies
|
|
52
|
+
params[:selectTags] = select_tags if select_tags
|
|
53
|
+
params[:filter] = filter if filter.present?
|
|
54
|
+
@client.api_request(method: "trigger.get", params: params)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 按主机、描述和可选标签查询唯一触发器。
|
|
58
|
+
# @return [Hash, nil]
|
|
59
|
+
def find_for_host(hostid:, description:, tags: nil)
|
|
60
|
+
params = {
|
|
61
|
+
hostids: hostid,
|
|
62
|
+
filter: { description: description },
|
|
63
|
+
output: ["triggerid", "description"],
|
|
64
|
+
selectTags: "extend"
|
|
21
65
|
}
|
|
66
|
+
params[:tags] = tags if tags.present?
|
|
67
|
+
result = @client.api_request(
|
|
68
|
+
method: "trigger.get",
|
|
69
|
+
params: params
|
|
70
|
+
)
|
|
71
|
+
raise Conflict, "multiple triggers match #{description} on host #{hostid}" if result.length > 1
|
|
72
|
+
|
|
73
|
+
result.first
|
|
22
74
|
end
|
|
23
75
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
76
|
+
# 按管理标签查询唯一触发器。
|
|
77
|
+
# @return [Hash, nil]
|
|
78
|
+
def find_managed_for_host(hostid:, managed_key:)
|
|
79
|
+
result = @client.api_request(
|
|
80
|
+
method: "trigger.get",
|
|
81
|
+
params: {
|
|
82
|
+
hostids: hostid,
|
|
83
|
+
tags: [{ tag: "zabbix_manager_id", value: managed_key, operator: 1 }],
|
|
84
|
+
output: ["triggerid", "description"],
|
|
85
|
+
selectTags: "extend"
|
|
34
86
|
}
|
|
35
87
|
)
|
|
88
|
+
raise Conflict, "multiple triggers use managed key #{managed_key}" if result.length > 1
|
|
89
|
+
|
|
90
|
+
result.first
|
|
36
91
|
end
|
|
37
92
|
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
93
|
+
# 按触发器 ID 查询唯一对象,并可返回当前依赖集合。
|
|
94
|
+
# @return [Hash, nil]
|
|
95
|
+
def find_by_id(triggerid, select_dependencies: nil)
|
|
96
|
+
id = normalized_ids([triggerid]).first
|
|
97
|
+
params = { triggerids: id, output: "extend" }
|
|
98
|
+
params[:selectDependencies] = select_dependencies if select_dependencies
|
|
99
|
+
result = @client.api_request(method: "trigger.get", params: params)
|
|
100
|
+
raise Conflict, "multiple triggers use triggerid #{id}" if result.length > 1
|
|
41
101
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
dump_by_id(key.to_sym => data[key.to_sym]).each do |item|
|
|
45
|
-
dump = symbolize_keys(item) if item[key].to_i == data[key.to_sym].to_i
|
|
46
|
-
end
|
|
102
|
+
result.first
|
|
103
|
+
end
|
|
47
104
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
105
|
+
# 按稳定管理键串行创建或更新主机触发器。
|
|
106
|
+
# 可通过 Client 的 upsert_lock 接入跨进程锁。
|
|
107
|
+
# @return [Integer]
|
|
108
|
+
def upsert_for_host(data)
|
|
109
|
+
attributes = data.deep_symbolize_keys
|
|
110
|
+
lock_key = "trigger:#{attributes[:hostid]}:#{attributes[:managed_key] || attributes[:description]}"
|
|
111
|
+
@client.with_upsert_lock(lock_key) { perform_upsert_for_host(attributes) }
|
|
112
|
+
end
|
|
52
113
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
114
|
+
# 批量启用或停用触发器。
|
|
115
|
+
# @return [Array<Integer>]
|
|
116
|
+
def set_status(hostid:, triggerids:, enabled:)
|
|
117
|
+
ids = normalized_ids(triggerids)
|
|
118
|
+
verify_host_ownership!(hostid, ids)
|
|
119
|
+
result = @client.api_request(
|
|
120
|
+
method: "trigger.update",
|
|
121
|
+
params: ids.map { |triggerid| { triggerid: triggerid, status: enabled ? 0 : 1 } }
|
|
122
|
+
)
|
|
123
|
+
Array(result.fetch("triggerids")).map(&:to_i)
|
|
124
|
+
end
|
|
56
125
|
|
|
57
|
-
|
|
126
|
+
# 批量删除明确指定的触发器。
|
|
127
|
+
# @return [Array<Integer>]
|
|
128
|
+
def delete_many(hostid:, triggerids:)
|
|
129
|
+
ids = normalized_ids(triggerids)
|
|
130
|
+
verify_host_ownership!(hostid, ids)
|
|
131
|
+
result = @client.api_request(method: "trigger.delete", params: ids)
|
|
132
|
+
Array(result.fetch("triggerids")).map(&:to_i)
|
|
133
|
+
end
|
|
58
134
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
135
|
+
# 使用 trigger.update 完整替换一个触发器的依赖集合。
|
|
136
|
+
# 传入空集合可清除依赖,返回被更新的触发器 ID。
|
|
137
|
+
# @return [Integer]
|
|
138
|
+
def replace_dependencies(hostid:, triggerid:, depends_on:, allow_cross_host_dependencies: false)
|
|
139
|
+
id = normalized_ids([triggerid]).first
|
|
140
|
+
dependency_ids = normalized_ids(depends_on, allow_empty: true)
|
|
141
|
+
dependencies = dependency_ids.map { |value| { triggerid: value } }
|
|
142
|
+
raise Invalid, "trigger cannot depend on itself" if dependencies.any? { |item| item[:triggerid] == id }
|
|
143
|
+
|
|
144
|
+
verify_host_ownership!(hostid, [id])
|
|
145
|
+
verify_host_ownership!(hostid, dependency_ids) if dependency_ids.any? && !allow_cross_host_dependencies
|
|
146
|
+
update_dependencies(id, dependencies)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# 在保留现有依赖的前提下,为触发器追加一个或多个依赖。
|
|
150
|
+
# 读改写过程按触发器 ID 加锁,跨进程可通过 Client 的 upsert_lock 协调。
|
|
151
|
+
# @return [Integer]
|
|
152
|
+
def add_dependencies(hostid:, triggerid:, depends_on:, allow_cross_host_dependencies: false)
|
|
153
|
+
id = normalized_ids([triggerid]).first
|
|
154
|
+
additions = normalized_ids(depends_on)
|
|
155
|
+
raise Invalid, "trigger cannot depend on itself" if additions.include?(id)
|
|
156
|
+
|
|
157
|
+
verify_host_ownership!(hostid, [id])
|
|
158
|
+
|
|
159
|
+
@client.with_upsert_lock("trigger-dependencies:#{id}") do
|
|
160
|
+
current = find_by_id(id, select_dependencies: ["triggerid"])
|
|
161
|
+
raise ApiError, "Zabbix trigger #{id} was not found" unless current
|
|
162
|
+
|
|
163
|
+
existing = Array(current["dependencies"]).filter_map { |dependency| dependency["triggerid"] }
|
|
164
|
+
dependency_ids = (existing + additions).uniq
|
|
165
|
+
unless allow_cross_host_dependencies
|
|
166
|
+
verify_host_ownership!(hostid, dependency_ids)
|
|
167
|
+
end
|
|
168
|
+
dependencies = dependency_ids.map { |value| { triggerid: value } }
|
|
169
|
+
update_dependencies(id, dependencies)
|
|
72
170
|
end
|
|
73
171
|
end
|
|
74
172
|
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
173
|
+
# 执行已加锁的触发器幂等写入。
|
|
174
|
+
# @return [Integer]
|
|
175
|
+
# @api private
|
|
176
|
+
private def perform_upsert_for_host(data)
|
|
177
|
+
attributes = data.deep_symbolize_keys
|
|
178
|
+
hostid = attributes.delete(:hostid)
|
|
179
|
+
managed_key = attributes.delete(:managed_key)&.to_s
|
|
180
|
+
legacy_identity_tags = attributes.delete(:legacy_identity_tags)
|
|
181
|
+
raise Invalid, "hostid is required" if hostid.blank?
|
|
182
|
+
raise Invalid, "description is required" if attributes[:description].blank?
|
|
183
|
+
raise Invalid, "expression is required" if attributes[:expression].blank?
|
|
184
|
+
|
|
185
|
+
if managed_key.present?
|
|
186
|
+
raise Invalid, "managed_key is too long" if managed_key.length > 200
|
|
78
187
|
|
|
79
|
-
|
|
80
|
-
|
|
188
|
+
attributes[:tags] = Array(attributes[:tags]).reject do |tag|
|
|
189
|
+
tag[:tag].to_s == "zabbix_manager_id" || tag["tag"].to_s == "zabbix_manager_id"
|
|
190
|
+
end
|
|
191
|
+
attributes[:tags] << { tag: "zabbix_manager_id", value: managed_key }
|
|
192
|
+
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
|
|
81
200
|
else
|
|
82
|
-
|
|
201
|
+
ownership_tags = Array(attributes[:tags]).select do |tag|
|
|
202
|
+
tag[:tag].to_s == "managed_by" || tag["tag"].to_s == "managed_by"
|
|
203
|
+
end
|
|
204
|
+
current = find_for_host(hostid: hostid, description: attributes[:description], tags: ownership_tags)
|
|
205
|
+
end
|
|
206
|
+
if current
|
|
207
|
+
triggerid = current.fetch("triggerid")
|
|
208
|
+
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.to_i
|
|
211
|
+
else
|
|
212
|
+
create_managed_trigger(hostid, managed_key, attributes)
|
|
83
213
|
end
|
|
84
214
|
end
|
|
85
215
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
216
|
+
# 创建触发器;若响应丢失或并发冲突则按管理键回读收敛。
|
|
217
|
+
# @return [Integer]
|
|
218
|
+
# @api private
|
|
219
|
+
private def create_managed_trigger(hostid, managed_key, attributes)
|
|
220
|
+
result = @client.api_request(method: "trigger.create", params: attributes)
|
|
221
|
+
result.fetch("triggerids").first.to_i
|
|
222
|
+
rescue TransportError => original_error
|
|
223
|
+
current = recover_created_trigger(hostid: hostid, managed_key: managed_key) if managed_key.present?
|
|
224
|
+
return current.fetch("triggerid").to_i if current
|
|
225
|
+
|
|
226
|
+
raise ResultUnknown,
|
|
227
|
+
"trigger create result is unknown; inspect managed key #{managed_key.inspect} before retrying: " \
|
|
228
|
+
"#{original_error.message}"
|
|
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
|
|
90
234
|
end
|
|
91
235
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
236
|
+
# 对结果不确定的创建执行短暂退避回读,不重放写请求。
|
|
237
|
+
# @return [Hash, nil]
|
|
238
|
+
# @api private
|
|
239
|
+
private def recover_created_trigger(hostid:, managed_key:)
|
|
240
|
+
delays = @client.options.fetch(:uncertain_write_delays, DEFAULT_UNCERTAIN_WRITE_DELAYS)
|
|
241
|
+
delays.each do |delay|
|
|
242
|
+
sleep(delay) if delay.positive?
|
|
243
|
+
current = find_managed_for_host(hostid: hostid, managed_key: managed_key)
|
|
244
|
+
return current if current
|
|
245
|
+
rescue ApiError, TransportError
|
|
246
|
+
next
|
|
247
|
+
end
|
|
248
|
+
nil
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# 合并受管标签,同时保留调用方未覆盖的运维标签。
|
|
252
|
+
# @return [Array<Hash>]
|
|
253
|
+
# @api private
|
|
254
|
+
private def merge_tags(existing_tags, desired_tags)
|
|
255
|
+
desired = Array(desired_tags).map(&:deep_symbolize_keys)
|
|
256
|
+
desired_names = desired.map { |tag| tag.fetch(:tag).to_s }
|
|
257
|
+
retained = Array(existing_tags).map(&:deep_symbolize_keys).reject do |tag|
|
|
258
|
+
desired_names.include?(tag[:tag].to_s)
|
|
259
|
+
end
|
|
260
|
+
retained + desired
|
|
261
|
+
end
|
|
262
|
+
|
|
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
|
+
# 回查触发器归属,阻止共享高权限令牌跨主机修改。
|
|
274
|
+
private def verify_host_ownership!(hostid, ids)
|
|
275
|
+
raise Invalid, "hostid is required" if hostid.blank?
|
|
95
276
|
|
|
96
277
|
result = @client.api_request(
|
|
97
|
-
method: "trigger.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
278
|
+
method: "trigger.get", params: { hostids: hostid, triggerids: ids, output: ["triggerid"] }
|
|
279
|
+
)
|
|
280
|
+
owned_ids = result.map { |trigger| trigger.fetch("triggerid").to_s }
|
|
281
|
+
foreign_ids = ids - owned_ids
|
|
282
|
+
raise Conflict, "triggers do not belong to host #{hostid}: #{foreign_ids.join(", ")}" if foreign_ids.any?
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# 使用官方 trigger.update 完整写入依赖集合。
|
|
286
|
+
private def update_dependencies(triggerid, dependencies)
|
|
287
|
+
result = @client.api_request(
|
|
288
|
+
method: "trigger.update", params: { triggerid: triggerid, dependencies: dependencies }
|
|
102
289
|
)
|
|
103
|
-
result.
|
|
290
|
+
result.fetch("triggerids").first.to_i
|
|
104
291
|
end
|
|
105
292
|
end
|
|
106
293
|
end
|
|
@@ -2,22 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
class ZabbixManager
|
|
4
4
|
class Usergroups < Basic
|
|
5
|
-
#
|
|
5
|
+
# 返回 Zabbix API 中用户组对象的方法名前缀。
|
|
6
|
+
#
|
|
7
|
+
# @return [String] 用户组对象的方法名前缀
|
|
6
8
|
def method_name
|
|
7
9
|
"usergroup"
|
|
8
10
|
end
|
|
9
11
|
|
|
10
|
-
#
|
|
12
|
+
# 返回用户组对象的主键字段名。
|
|
13
|
+
#
|
|
14
|
+
# @return [String] 用户组对象的主键字段名
|
|
11
15
|
def key
|
|
12
16
|
"usrgrpid"
|
|
13
17
|
end
|
|
14
18
|
|
|
15
|
-
#
|
|
19
|
+
# 返回用于唯一识别用户组的业务字段名。
|
|
20
|
+
#
|
|
21
|
+
# @return [String] 用户组对象的业务标识字段名
|
|
16
22
|
def identify
|
|
17
23
|
"name"
|
|
18
24
|
end
|
|
19
25
|
|
|
20
|
-
#
|
|
26
|
+
# 更新用户组对主机组的访问权限。
|
|
27
|
+
#
|
|
28
|
+
# @param data [Hash] 包含 usrgrpid、hostgroupids 及可选 permission
|
|
29
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
30
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
31
|
+
# @return [Integer, nil] 已更新用户组的 ID
|
|
21
32
|
def permissions(data)
|
|
22
33
|
permission = data[:permission] || 2
|
|
23
34
|
result = @client.api_request(
|
|
@@ -30,12 +41,23 @@ class ZabbixManager
|
|
|
30
41
|
result ? result["usrgrpids"][0].to_i : nil
|
|
31
42
|
end
|
|
32
43
|
|
|
33
|
-
#
|
|
44
|
+
# 将用户加入用户组;兼容旧调用并委托给 update_users。
|
|
45
|
+
#
|
|
46
|
+
# @deprecated Zabbix 已移除 massAdd,请使用 update_users。
|
|
47
|
+
# @param data [Hash] 包含 userids 和 usrgrpids
|
|
48
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
49
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
50
|
+
# @return [Integer, nil] 已更新用户组的 ID
|
|
34
51
|
def add_user(data)
|
|
35
52
|
update_users(data)
|
|
36
53
|
end
|
|
37
54
|
|
|
38
|
-
#
|
|
55
|
+
# 批量替换指定用户组中的用户列表。
|
|
56
|
+
#
|
|
57
|
+
# @param data [Hash] 包含 userids 和 usrgrpids
|
|
58
|
+
# @raise [ApiError] Zabbix API 返回业务错误时抛出
|
|
59
|
+
# @raise [TransportError] Zabbix 服务返回非成功 HTTP 状态时抛出
|
|
60
|
+
# @return [Integer, nil] 首个已更新用户组的 ID
|
|
39
61
|
def update_users(data)
|
|
40
62
|
user_groups = data[:usrgrpids].map do |t|
|
|
41
63
|
{
|