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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -9
  3. data/LICENSE +1 -1
  4. data/README.md +202 -8
  5. data/lib/zabbix_manager/basic/basic_alias.rb +25 -3
  6. data/lib/zabbix_manager/basic/basic_func.rb +32 -61
  7. data/lib/zabbix_manager/basic/basic_init.rb +28 -13
  8. data/lib/zabbix_manager/basic/basic_logic.rb +172 -128
  9. data/lib/zabbix_manager/classes/actions.rb +21 -11
  10. data/lib/zabbix_manager/classes/applications.rb +12 -17
  11. data/lib/zabbix_manager/classes/configurations.rb +20 -5
  12. data/lib/zabbix_manager/classes/drules.rb +11 -23
  13. data/lib/zabbix_manager/classes/errors.rb +16 -28
  14. data/lib/zabbix_manager/classes/events.rb +6 -2
  15. data/lib/zabbix_manager/classes/graphs.rb +41 -34
  16. data/lib/zabbix_manager/classes/hostgroups.rb +53 -3
  17. data/lib/zabbix_manager/classes/hostinterfaces.rb +172 -12
  18. data/lib/zabbix_manager/classes/hosts.rb +197 -68
  19. data/lib/zabbix_manager/classes/httptests.rb +18 -20
  20. data/lib/zabbix_manager/classes/items.rb +199 -72
  21. data/lib/zabbix_manager/classes/maintenance.rb +6 -2
  22. data/lib/zabbix_manager/classes/mediatypes.rb +7 -63
  23. data/lib/zabbix_manager/classes/problems.rb +75 -57
  24. data/lib/zabbix_manager/classes/proxies.rb +48 -7
  25. data/lib/zabbix_manager/classes/roles.rb +47 -36
  26. data/lib/zabbix_manager/classes/screens.rb +45 -14
  27. data/lib/zabbix_manager/classes/scripts.rb +17 -7
  28. data/lib/zabbix_manager/classes/server.rb +7 -1
  29. data/lib/zabbix_manager/classes/templates.rb +59 -21
  30. data/lib/zabbix_manager/classes/triggers.rb +253 -66
  31. data/lib/zabbix_manager/classes/usergroups.rb +28 -6
  32. data/lib/zabbix_manager/classes/usermacros.rb +119 -33
  33. data/lib/zabbix_manager/classes/users.rb +39 -7
  34. data/lib/zabbix_manager/classes/valuemaps.rb +13 -19
  35. data/lib/zabbix_manager/client.rb +311 -135
  36. data/lib/zabbix_manager/http_transport.rb +192 -0
  37. data/lib/zabbix_manager/log_sanitizer.rb +68 -0
  38. data/lib/zabbix_manager/monitoring.rb +691 -0
  39. data/lib/zabbix_manager/version.rb +1 -1
  40. data/lib/zabbix_manager.rb +118 -40
  41. data/zabbix_manager.gemspec +21 -8
  42. metadata +208 -22
  43. 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
- # 用于通过 Zabbix API 标识特定模板对象的 id 字段名称
12
+ # 返回用于唯一识别模板的业务字段名。
13
+ #
14
+ # @return [String] 模板对象的业务标识字段名
11
15
  def identify
12
16
  "host"
13
17
  end
14
18
 
15
- # 通过 Zabbix API 删除模板对象
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
- # 从 Zabbix API 获取主机的模板 id
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
- # 通过 Zabbix API 获取或创建模板对象
29
- # def get_or_create(data)
30
- # if (templateid = get_id(host: data[:host]))
31
- # templateid
32
- # else
33
- # create(data)
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: data[:hosts_id].map { |t| { hostid: t } },
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
- # 通过 Zabbix API 批量添加模板到主机
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: data[:hosts_id].map { |t| { hostid: t } },
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
- # 通过 Zabbix API 批量从主机移除模板
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: data[:hosts_id],
86
+ hostids: data[:hosts_id],
67
87
  templateids: data[:templates_id],
68
- groupids: data[:group_id],
69
- force: 1
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
- # 与 Zabbix API 交互的触发器方法名称
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
- # 用于通过 Zabbix API 标识特定触发器对象的 id 字段名称
13
+ # 使用描述作为通用 CRUD 的业务标识。
14
+ #
15
+ # @return [String]
11
16
  def identify
12
17
  "description"
13
18
  end
14
19
 
15
- # 创建 Zabbix 触发器的默认选项
16
- def default_options
17
- {
18
- recovery_mode: 1,
19
- type: 0,
20
- manual_close: 1
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
- # 通过键值对从 Zabbix API 转储触发器对象数据
25
- def dump_by_id(data)
26
- log "[DEBUG] 调用 dump_by_id 方法,参数: #{data.inspect}"
27
-
28
- get_raw(
29
- {
30
- triggerids: data[keys.to_sym],
31
- output: "extend",
32
- select_items: "extend",
33
- select_functions: "extend"
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
- # 安全地使用 Zabbix API 更新触发器对象,通过删除和替换触发器
39
- def safe_update(data)
40
- log "[DEBUG] 调用 safe_update 方法,参数: #{data.inspect}"
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
- dump = {}
43
- item_id = data[key.to_sym].to_i
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
- expression = "#{dump[:items][0][:key_]}.#{dump[:functions][0][:function]}(#{dump[:functions][0][:parameter]})"
49
- dump[:expression] = dump[:expression].gsub(/\{(\d*)\}/, "{#{expression}}") # TODO: ugly regexp
50
- dump.delete(:functions)
51
- dump.delete(:items)
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
- old_expression = data[:expression]
54
- data[:expression] = data[:expression].gsub(/\{.*:/, "{") # TODO: ugly regexp
55
- data.delete(:templateid)
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
- log "[DEBUG] expression: #{dump[:expression]}\n data: #{data[:expression]}"
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
- if hash_equals?(dump, data)
60
- log "[DEBUG] 键 #{dump} 和 #{data} 相等,跳过 safe_update"
61
- item_id
62
- else
63
- data[:expression] = old_expression
64
- # disable old trigger
65
- log "[DEBUG] disable :" + @client.api_request(method: "#{method_name}.update",
66
- params: [{
67
- triggerid: data[:triggerid], status: "1"
68
- }]).inspect
69
- # create new trigger
70
- data.delete(:triggerid)
71
- create(data)
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
- # 通过 Zabbix API 获取或创建触发器对象
76
- def get_or_create(data)
77
- log "[DEBUG] 调用 #{method_name}.get_or_create 方法,参数: #{data.inspect}"
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
- if (id = get_id(description: data[:description], hostid: data[:hostid]))
80
- id
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
- create(data)
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
- # 使用 Zabbix API 创建或更新触发器对象
87
- def create_or_update(data)
88
- triggerid = get_id(description: data[:description], hostid: data[:hostid])
89
- triggerid ? update(data.merge(triggerid: triggerid)) : create(data)
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
- # 使用 Zabbix API 添加触发器依赖关系
93
- def add_trigger_dependency(triggerid, depend_on_trigger_id)
94
- log "[DEBUG] 调用 add_trigger_dependency 方法,参数: #{triggerid} #{depend_on_trigger_id}"
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.adddependencies",
98
- params: {
99
- triggerid: triggerid,
100
- dependsOnTriggerid: depend_on_trigger_id
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.empty? ? nil : result["triggerids"]
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
- # 用于通过 Zabbix API 与 Usergroups 交互的方法名称
5
+ # 返回 Zabbix API 中用户组对象的方法名前缀。
6
+ #
7
+ # @return [String] 用户组对象的方法名前缀
6
8
  def method_name
7
9
  "usergroup"
8
10
  end
9
11
 
10
- # 用于 Usergroup 对象通过 Zabbix API 的键字段名称
12
+ # 返回用户组对象的主键字段名。
13
+ #
14
+ # @return [String] 用户组对象的主键字段名
11
15
  def key
12
16
  "usrgrpid"
13
17
  end
14
18
 
15
- # 用于通过 Zabbix API 识别特定 Usergroup 对象的 id 字段名称
19
+ # 返回用于唯一识别用户组的业务字段名。
20
+ #
21
+ # @return [String] 用户组对象的业务标识字段名
16
22
  def identify
17
23
  "name"
18
24
  end
19
25
 
20
- # 使用 Zabbix API 设置用户组的权限
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
- # 使用 Zabbix API 将用户添加到用户组
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
- # 使用 Zabbix API 更新用户组中的用户
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
  {