整合 /srv/监控架构优化方案 核心代码: 后端模型(4个新文件): - monitor.go: MonitorTarget(统一监控目标) + MonitorMetric(指标定义) + MetricValue(时序数据) + MetricHistory - snmp.go: SNMPDevice + SNMPTemplate + OID模板管理 - site.go: SiteTask(拨测任务) + SiteResult(拨测结果) - alert_v2.go: 增强告警(多条件规则/模板/升级策略/静默) 后端 pkg(3个包): - pkg/prometheus/client.go (531行): PromQL查询/Range/Labels/Rules - pkg/zabbix/client.go: Zabbix API (Host/Item/Trigger/History) - pkg/snmp/collector.go (621行): gosnmp Walk/BulkWalk/自动发现 基础设施: - Dockerfile: Go 1.21→1.24 (gosnmp依赖) - Agent安装弹框: SSH一键安装Tab + server上下文 - 服务器列表操作栏: 安装按钮条件显示/隐藏 Handler层因类型不兼容暂时跳过,模型和客户端包已编译通过。
757 lines
23 KiB
Go
757 lines
23 KiB
Go
package zabbix
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ============================================
|
|
// Zabbix API 客户端
|
|
// ============================================
|
|
|
|
// Client Zabbix API客户端
|
|
// 封装对Zabbix JSON-RPC API的调用,支持认证、主机管理、监控项查询等
|
|
// 兼容 Zabbix 5.0 / 6.0 / 7.0 版本
|
|
type Client struct {
|
|
baseURL string // Zabbix API地址,如 http://zabbix.example.com/api_jsonrpc.php
|
|
username string // Zabbix用户名
|
|
password string // Zabbix密码
|
|
authToken string // 认证令牌
|
|
httpClient *http.Client // HTTP客户端
|
|
mu sync.RWMutex // 保护authToken的互斥锁
|
|
}
|
|
|
|
// ClientOption 客户端配置选项
|
|
type ClientOption func(*Client)
|
|
|
|
// WithHTTPTimeout 设置HTTP超时
|
|
func WithHTTPTimeout(timeout time.Duration) ClientOption {
|
|
return func(c *Client) {
|
|
c.httpClient.Timeout = timeout
|
|
}
|
|
}
|
|
|
|
// WithToken 使用已有认证令牌
|
|
func WithToken(token string) ClientOption {
|
|
return func(c *Client) {
|
|
c.authToken = token
|
|
}
|
|
}
|
|
|
|
// NewClient 创建Zabbix客户端
|
|
// baseURL: Zabbix API地址,如 http://zabbix.example.com/api_jsonrpc.php
|
|
func NewClient(baseURL, username, password string, opts ...ClientOption) *Client {
|
|
c := &Client{
|
|
baseURL: baseURL,
|
|
username: username,
|
|
password: password,
|
|
httpClient: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
for _, opt := range opts {
|
|
opt(c)
|
|
}
|
|
return c
|
|
}
|
|
|
|
// ============================================
|
|
// Zabbix JSON-RPC 请求/响应结构
|
|
// ============================================
|
|
|
|
// Request Zabbix API请求
|
|
type Request struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
Method string `json:"method"`
|
|
Params interface{} `json:"params"`
|
|
Auth string `json:"auth,omitempty"`
|
|
ID int `json:"id"`
|
|
}
|
|
|
|
// Response Zabbix API响应
|
|
type Response struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
Result json.RawMessage `json:"result,omitempty"`
|
|
Error *APIError `json:"error,omitempty"`
|
|
ID int `json:"id"`
|
|
}
|
|
|
|
// APIError Zabbix API错误
|
|
type APIError struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data string `json:"data"`
|
|
}
|
|
|
|
// Error 实现error接口
|
|
func (e *APIError) Error() string {
|
|
return fmt.Sprintf("Zabbix API Error [code:%d]: %s - %s", e.Code, e.Message, e.Data)
|
|
}
|
|
|
|
// LoginResult 登录结果
|
|
type LoginResult struct {
|
|
Token string `json:"result"`
|
|
}
|
|
|
|
// ============================================
|
|
// 内部请求方法
|
|
// ============================================
|
|
|
|
var requestIDCounter int
|
|
var requestIDMu sync.Mutex
|
|
|
|
func nextRequestID() int {
|
|
requestIDMu.Lock()
|
|
defer requestIDMu.Unlock()
|
|
requestIDCounter++
|
|
return requestIDCounter
|
|
}
|
|
|
|
// call 执行JSON-RPC调用
|
|
func (c *Client) call(ctx context.Context, method string, params interface{}, result interface{}) error {
|
|
req := Request{
|
|
JSONRPC: "2.0",
|
|
Method: method,
|
|
Params: params,
|
|
ID: nextRequestID(),
|
|
}
|
|
|
|
// 非登录请求需要认证
|
|
if method != "user.login" {
|
|
c.mu.RLock()
|
|
token := c.authToken
|
|
c.mu.RUnlock()
|
|
if token == "" {
|
|
return fmt.Errorf("not authenticated, call Auth() first")
|
|
}
|
|
req.Auth = token
|
|
}
|
|
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal request failed: %w", err)
|
|
}
|
|
|
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL, bytes.NewReader(body))
|
|
if err != nil {
|
|
return fmt.Errorf("create request failed: %w", err)
|
|
}
|
|
httpReq.Header.Set("Content-Type", "application/json-rpc")
|
|
|
|
httpResp, err := c.httpClient.Do(httpReq)
|
|
if err != nil {
|
|
return fmt.Errorf("HTTP request failed: %w", err)
|
|
}
|
|
defer httpResp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(httpResp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("read response body failed: %w", err)
|
|
}
|
|
|
|
if httpResp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("unexpected HTTP status: %d, body: %s", httpResp.StatusCode, string(respBody))
|
|
}
|
|
|
|
var resp Response
|
|
if err := json.Unmarshal(respBody, &resp); err != nil {
|
|
return fmt.Errorf("unmarshal response failed: %w", err)
|
|
}
|
|
|
|
if resp.Error != nil {
|
|
// 处理会话过期,自动重试一次
|
|
if resp.Error.Code == -32602 || resp.Error.Message == "Session terminated" {
|
|
if err := c.Auth(ctx); err != nil {
|
|
return fmt.Errorf("session expired, re-auth failed: %w", err)
|
|
}
|
|
// 重试请求
|
|
return c.call(ctx, method, params, result)
|
|
}
|
|
return resp.Error
|
|
}
|
|
|
|
if result != nil {
|
|
if err := json.Unmarshal(resp.Result, result); err != nil {
|
|
return fmt.Errorf("unmarshal result failed: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ============================================
|
|
// 认证
|
|
// ============================================
|
|
|
|
// Auth 执行Zabbix认证,获取会话令牌
|
|
// 支持 Zabbix 5.0/6.0/7.0 的认证方式
|
|
func (c *Client) Auth(ctx context.Context) error {
|
|
// Zabbix 6.0+ 使用 user.login
|
|
params := map[string]interface{}{
|
|
"username": c.username,
|
|
"password": c.password,
|
|
}
|
|
|
|
var result string
|
|
if err := c.call(ctx, "user.login", params, &result); err != nil {
|
|
// 尝试旧版API (Zabbix 5.0)
|
|
oldParams := map[string]interface{}{
|
|
"user": c.username,
|
|
"password": c.password,
|
|
}
|
|
if err := c.call(ctx, "user.login", oldParams, &result); err != nil {
|
|
return fmt.Errorf("authentication failed: %w", err)
|
|
}
|
|
}
|
|
|
|
c.mu.Lock()
|
|
c.authToken = result
|
|
c.mu.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Logout 登出并清除会话
|
|
func (c *Client) Logout(ctx context.Context) error {
|
|
if err := c.call(ctx, "user.logout", []string{}, nil); err != nil {
|
|
return fmt.Errorf("logout failed: %w", err)
|
|
}
|
|
c.mu.Lock()
|
|
c.authToken = ""
|
|
c.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
// GetAuthToken 获取当前认证令牌
|
|
func (c *Client) GetAuthToken() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.authToken
|
|
}
|
|
|
|
// ============================================
|
|
// 主机管理
|
|
// ============================================
|
|
|
|
// Host 主机信息
|
|
type Host struct {
|
|
HostID string `json:"hostid"`
|
|
Host string `json:"host"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"` // 0=监控 1=未监控
|
|
Available string `json:"available"` // 0=未知 1=可用 2=不可用
|
|
Error string `json:"error"` // 可用性错误信息
|
|
Interfaces []HostInterface `json:"interfaces,omitempty"`
|
|
Groups []HostGroup `json:"groups,omitempty"`
|
|
Templates []Template `json:"parentTemplates,omitempty"`
|
|
Tags []Tag `json:"tags,omitempty"`
|
|
Macros []Macro `json:"macros,omitempty"`
|
|
Inventory map[string]string `json:"inventory,omitempty"`
|
|
Description string `json:"description"`
|
|
ProxyHostID string `json:"proxy_hostid"`
|
|
}
|
|
|
|
// HostInterface 主机接口
|
|
type HostInterface struct {
|
|
InterfaceID string `json:"interfaceid"`
|
|
HostID string `json:"hostid"`
|
|
Main string `json:"main"` // 0=否 1=是
|
|
Type string `json:"type"` // 1=Agent 2=SNMP 3=IPMI 4=JMX
|
|
UseIP string `json:"useip"` // 0=DNS 1=IP
|
|
IP string `json:"ip"`
|
|
DNS string `json:"dns"`
|
|
Port string `json:"port"`
|
|
}
|
|
|
|
// HostGroup 主机组
|
|
type HostGroup struct {
|
|
GroupID string `json:"groupid"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// Template 模板
|
|
type Template struct {
|
|
TemplateID string `json:"templateid"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// Tag 标签
|
|
type Tag struct {
|
|
Tag string `json:"tag"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// Macro 宏
|
|
type Macro struct {
|
|
Macro string `json:"macro"`
|
|
Value string `json:"value"`
|
|
TypeID string `json:"type,string"`
|
|
}
|
|
|
|
// GetHostsParams 获取主机列表参数
|
|
type GetHostsParams struct {
|
|
HostIDs []string `json:"hostids,omitempty"`
|
|
GroupIDs []string `json:"groupids,omitempty"`
|
|
TemplateIDs []string `json:"templateids,omitempty"`
|
|
ProxyIDs []string `json:"proxyids,omitempty"`
|
|
Status int `json:"status,omitempty"` // 0=监控 1=未监控
|
|
Filter map[string]interface{} `json:"filter,omitempty"`
|
|
Search map[string]interface{} `json:"search,omitempty"`
|
|
SearchInventory map[string]interface{} `json:"searchInventory,omitempty"`
|
|
Output []string `json:"output,omitempty"`
|
|
SelectInterfaces []string `json:"selectInterfaces,omitempty"`
|
|
SelectGroups []string `json:"selectGroups,omitempty"`
|
|
SelectParentTemplates []string `json:"selectParentTemplates,omitempty"`
|
|
SelectTags []string `json:"selectTags,omitempty"`
|
|
SelectMacros []string `json:"selectMacros,omitempty"`
|
|
SelectInventory []string `json:"selectInventory,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
SortField []string `json:"sortfield,omitempty"`
|
|
SortOrder []string `json:"sortorder,omitempty"`
|
|
}
|
|
|
|
// GetHosts 获取主机列表
|
|
// params: 查询参数,为nil时返回所有主机
|
|
func (c *Client) GetHosts(ctx context.Context, params *GetHostsParams) ([]Host, error) {
|
|
if params == nil {
|
|
params = &GetHostsParams{}
|
|
}
|
|
if len(params.Output) == 0 {
|
|
params.Output = []string{"hostid", "host", "name", "status", "available", "error", "description", "proxy_hostid"}
|
|
}
|
|
|
|
var result []Host
|
|
if err := c.call(ctx, "host.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get hosts failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetHostByID 通过ID获取主机
|
|
func (c *Client) GetHostByID(ctx context.Context, hostID string) (*Host, error) {
|
|
params := &GetHostsParams{
|
|
HostIDs: []string{hostID},
|
|
SelectInterfaces: []string{"interfaceid", "hostid", "main", "type", "useip", "ip", "dns", "port"},
|
|
SelectGroups: []string{"groupid", "name"},
|
|
SelectParentTemplates: []string{"templateid", "name"},
|
|
SelectTags: []string{"tag", "value"},
|
|
}
|
|
|
|
hosts, err := c.GetHosts(ctx, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(hosts) == 0 {
|
|
return nil, fmt.Errorf("host not found: %s", hostID)
|
|
}
|
|
return &hosts[0], nil
|
|
}
|
|
|
|
// ============================================
|
|
// 监控项管理
|
|
// ============================================
|
|
|
|
// Item 监控项信息
|
|
type Item struct {
|
|
ItemID string `json:"itemid"`
|
|
HostID string `json:"hostid"`
|
|
Name string `json:"name"`
|
|
Key string `json:"key_"`
|
|
Type string `json:"type"` // 0=Zabbix Agent 1=SNMPv1 2=Zabbix Trapper ...
|
|
ValueType string `json:"value_type"` // 0=Float 1=String 2=Log 3=Unsigned 4=Text
|
|
Delay string `json:"delay"` // 更新间隔
|
|
History string `json:"history"` // 历史数据保留时长
|
|
Trends string `json:"trends"` // 趋势数据保留时长
|
|
Status string `json:"status"` // 0=启用 1=禁用
|
|
Units string `json:"units"` // 单位
|
|
LastValue string `json:"lastvalue,omitempty"`
|
|
PrevValue string `json:"prevvalue,omitempty"`
|
|
LastClock string `json:"lastclock,omitempty"`
|
|
Description string `json:"description"`
|
|
Error string `json:"error"`
|
|
Tags []Tag `json:"tags,omitempty"`
|
|
// Zabbix 6.0+
|
|
MasterItemID string `json:"master_itemid,omitempty"`
|
|
Timeout string `json:"timeout,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
}
|
|
|
|
// GetItemsParams 获取监控项参数
|
|
type GetItemsParams struct {
|
|
ItemIDs []string `json:"itemids,omitempty"`
|
|
HostIDs []string `json:"hostids,omitempty"`
|
|
GroupIDs []string `json:"groupids,omitempty"`
|
|
TemplateIDs []string `json:"templateids,omitempty"`
|
|
Filter map[string]interface{} `json:"filter,omitempty"`
|
|
Search map[string]interface{} `json:"search,omitempty"`
|
|
Output []string `json:"output,omitempty"`
|
|
SelectTags []string `json:"selectTags,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
SortField []string `json:"sortfield,omitempty"`
|
|
SortOrder []string `json:"sortorder,omitempty"`
|
|
WebItems int `json:"webitems,omitempty"`
|
|
}
|
|
|
|
// GetItems 获取监控项列表
|
|
// params: 查询参数
|
|
func (c *Client) GetItems(ctx context.Context, params *GetItemsParams) ([]Item, error) {
|
|
if params == nil {
|
|
params = &GetItemsParams{}
|
|
}
|
|
if len(params.Output) == 0 {
|
|
params.Output = []string{"itemid", "hostid", "name", "key_", "type", "value_type", "delay", "history", "trends", "status", "units", "lastvalue", "lastclock", "description", "error"}
|
|
}
|
|
|
|
var result []Item
|
|
if err := c.call(ctx, "item.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get items failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetItemsByHost 获取指定主机的所有监控项
|
|
func (c *Client) GetItemsByHost(ctx context.Context, hostID string) ([]Item, error) {
|
|
params := &GetItemsParams{
|
|
HostIDs: []string{hostID},
|
|
SortField: []string{"name"},
|
|
}
|
|
return c.GetItems(ctx, params)
|
|
}
|
|
|
|
// ============================================
|
|
// 触发器管理
|
|
// ============================================
|
|
|
|
// Trigger 触发器信息
|
|
type Trigger struct {
|
|
TriggerID string `json:"triggerid"`
|
|
Expression string `json:"expression"`
|
|
Description string `json:"description"`
|
|
URL string `json:"url"`
|
|
Status string `json:"status"` // 0=启用 1=禁用
|
|
Value string `json:"value"` // 0=正常 1=问题
|
|
Priority string `json:"priority"` // 0=未分类 1=信息 2=警告 3=一般 4=严重 5=灾难
|
|
LastChange string `json:"lastchange"`
|
|
Comments string `json:"comments"`
|
|
Error string `json:"error"`
|
|
TemplateID string `json:"templateid"`
|
|
Type string `json:"type"` // 0=普通 1=多重问题
|
|
Tags []Tag `json:"tags,omitempty"`
|
|
Hosts []Host `json:"hosts,omitempty"`
|
|
// Zabbix 6.0+
|
|
OpData string `json:"opdata,omitempty"`
|
|
EventName string `json:"event_name,omitempty"`
|
|
// Zabbix 7.0+
|
|
State string `json:"state,omitempty"` // 0=正常 1=未知
|
|
}
|
|
|
|
// GetTriggersParams 获取触发器参数
|
|
type GetTriggersParams struct {
|
|
TriggerIDs []string `json:"triggerids,omitempty"`
|
|
HostIDs []string `json:"hostids,omitempty"`
|
|
GroupIDs []string `json:"groupids,omitempty"`
|
|
TemplateIDs []string `json:"templateids,omitempty"`
|
|
ItemIDs []string `json:"itemids,omitempty"`
|
|
Filter map[string]interface{} `json:"filter,omitempty"`
|
|
Search map[string]interface{} `json:"search,omitempty"`
|
|
Output []string `json:"output,omitempty"`
|
|
SelectHosts []string `json:"selectHosts,omitempty"`
|
|
SelectItems []string `json:"selectItems,omitempty"`
|
|
SelectTags []string `json:"selectTags,omitempty"`
|
|
ExpandExpression bool `json:"expandExpression,omitempty"`
|
|
ExpandDescription bool `json:"expandDescription,omitempty"`
|
|
SkipDependent bool `json:"skipDependent,omitempty"`
|
|
Active bool `json:"active,omitempty"`
|
|
WithLastEventUnacknowledged bool `json:"withLastEventUnacknowledged,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
SortField []string `json:"sortfield,omitempty"`
|
|
SortOrder []string `json:"sortorder,omitempty"`
|
|
MinSeverity int `json:"min_severity,omitempty"`
|
|
}
|
|
|
|
// GetTriggers 获取触发器列表
|
|
// params: 查询参数
|
|
func (c *Client) GetTriggers(ctx context.Context, params *GetTriggersParams) ([]Trigger, error) {
|
|
if params == nil {
|
|
params = &GetTriggersParams{}
|
|
}
|
|
if len(params.Output) == 0 {
|
|
params.Output = []string{"triggerid", "expression", "description", "url", "status", "value", "priority", "lastchange", "comments", "error"}
|
|
}
|
|
|
|
var result []Trigger
|
|
if err := c.call(ctx, "trigger.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get triggers failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetTriggersByHost 获取指定主机的触发器
|
|
func (c *Client) GetTriggersByHost(ctx context.Context, hostID string, onlyProblems bool) ([]Trigger, error) {
|
|
params := &GetTriggersParams{
|
|
HostIDs: []string{hostID},
|
|
SelectHosts: []string{"hostid", "host", "name"},
|
|
SelectTags: []string{"tag", "value"},
|
|
SortField: []string{"priority"},
|
|
SortOrder: []string{"DESC"},
|
|
}
|
|
if onlyProblems {
|
|
params.Filter = map[string]interface{}{
|
|
"value": "1", // 只查询有问题状态的触发器
|
|
}
|
|
}
|
|
return c.GetTriggers(ctx, params)
|
|
}
|
|
|
|
// ============================================
|
|
// 历史数据查询
|
|
// ============================================
|
|
|
|
// HistoryValue 历史数据值
|
|
type HistoryValue struct {
|
|
ItemID string `json:"itemid"`
|
|
Clock string `json:"clock"` // 时间戳
|
|
Value string `json:"value"` // 值
|
|
NS string `json:"ns"` // 纳秒
|
|
}
|
|
|
|
// HistoryData 解析后的历史数据
|
|
type HistoryData struct {
|
|
ItemID string
|
|
Timestamp time.Time
|
|
Value float64
|
|
}
|
|
|
|
// GetHistoryParams 获取历史数据参数
|
|
type GetHistoryParams struct {
|
|
History int `json:"history"` // 值类型: 0=Float 1=String 2=Log 3=Unsigned 4=Text 5=Snapshot
|
|
ItemIDs []string `json:"itemids"`
|
|
HostIDs []string `json:"hostids,omitempty"`
|
|
TimeFrom int64 `json:"time_from,omitempty"`
|
|
TimeTill int64 `json:"time_till,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
SortField string `json:"sortfield,omitempty"` // clock/itemid
|
|
SortOrder string `json:"sortorder,omitempty"` // ASC/DESC
|
|
}
|
|
|
|
// GetHistory 获取历史数据
|
|
// params: 查询参数
|
|
func (c *Client) GetHistory(ctx context.Context, params *GetHistoryParams) ([]HistoryValue, error) {
|
|
if params == nil || len(params.ItemIDs) == 0 {
|
|
return nil, fmt.Errorf("item IDs are required")
|
|
}
|
|
|
|
var result []HistoryValue
|
|
if err := c.call(ctx, "history.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get history failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetHistoryFloat 获取浮点型历史数据
|
|
func (c *Client) GetHistoryFloat(ctx context.Context, itemID string, from, till time.Time, limit int) ([]HistoryValue, error) {
|
|
params := &GetHistoryParams{
|
|
History: 0, // Float
|
|
ItemIDs: []string{itemID},
|
|
TimeFrom: from.Unix(),
|
|
TimeTill: till.Unix(),
|
|
Limit: limit,
|
|
SortField: "clock",
|
|
SortOrder: "DESC",
|
|
}
|
|
return c.GetHistory(ctx, params)
|
|
}
|
|
|
|
// GetHistoryUnsigned 获取无符号整型历史数据
|
|
func (c *Client) GetHistoryUnsigned(ctx context.Context, itemID string, from, till time.Time, limit int) ([]HistoryValue, error) {
|
|
params := &GetHistoryParams{
|
|
History: 3, // Unsigned
|
|
ItemIDs: []string{itemID},
|
|
TimeFrom: from.Unix(),
|
|
TimeTill: till.Unix(),
|
|
Limit: limit,
|
|
SortField: "clock",
|
|
SortOrder: "DESC",
|
|
}
|
|
return c.GetHistory(ctx, params)
|
|
}
|
|
|
|
// GetHistoryByItems 批量获取多个监控项的历史数据
|
|
func (c *Client) GetHistoryByItems(ctx context.Context, itemIDs []string, valueType int, from, till time.Time, limit int) ([]HistoryValue, error) {
|
|
params := &GetHistoryParams{
|
|
History: valueType,
|
|
ItemIDs: itemIDs,
|
|
TimeFrom: from.Unix(),
|
|
TimeTill: till.Unix(),
|
|
Limit: limit,
|
|
SortField: "clock",
|
|
SortOrder: "DESC",
|
|
}
|
|
return c.GetHistory(ctx, params)
|
|
}
|
|
|
|
// ============================================
|
|
// 主机组管理
|
|
// ============================================
|
|
|
|
// GetGroupsParams 获取主机组参数
|
|
type GetGroupsParams struct {
|
|
GroupIDs []string `json:"groupids,omitempty"`
|
|
HostIDs []string `json:"hostids,omitempty"`
|
|
TemplateIDs []string `json:"templateids,omitempty"`
|
|
TriggerIDs []string `json:"triggerids,omitempty"`
|
|
Filter map[string]interface{} `json:"filter,omitempty"`
|
|
Search map[string]interface{} `json:"search,omitempty"`
|
|
Output []string `json:"output,omitempty"`
|
|
SelectHosts []string `json:"selectHosts,omitempty"`
|
|
SelectTemplates []string `json:"selectTemplates,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
SortField []string `json:"sortfield,omitempty"`
|
|
SortOrder []string `json:"sortorder,omitempty"`
|
|
}
|
|
|
|
// GetGroups 获取主机组列表
|
|
// params: 查询参数
|
|
func (c *Client) GetGroups(ctx context.Context, params *GetGroupsParams) ([]HostGroup, error) {
|
|
if params == nil {
|
|
params = &GetGroupsParams{}
|
|
}
|
|
if len(params.Output) == 0 {
|
|
params.Output = []string{"groupid", "name"}
|
|
}
|
|
|
|
var result []HostGroup
|
|
if err := c.call(ctx, "hostgroup.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get groups failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetGroupByID 通过ID获取主机组
|
|
func (c *Client) GetGroupByID(ctx context.Context, groupID string) (*HostGroup, error) {
|
|
params := &GetGroupsParams{
|
|
GroupIDs: []string{groupID},
|
|
}
|
|
groups, err := c.GetGroups(ctx, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(groups) == 0 {
|
|
return nil, fmt.Errorf("group not found: %s", groupID)
|
|
}
|
|
return &groups[0], nil
|
|
}
|
|
|
|
// ============================================
|
|
// 趋势数据查询
|
|
// ============================================
|
|
|
|
// TrendValue 趋势数据
|
|
type TrendValue struct {
|
|
ItemID string `json:"itemid"`
|
|
Clock string `json:"clock"` // 小时开始时间戳
|
|
Num string `json:"num"` // 数据点数
|
|
ValueMin string `json:"value_min"`
|
|
ValueAvg string `json:"value_avg"`
|
|
ValueMax string `json:"value_max"`
|
|
}
|
|
|
|
// GetTrendsParams 获取趋势数据参数
|
|
type GetTrendsParams struct {
|
|
ItemIDs []string `json:"itemids"`
|
|
TimeFrom int64 `json:"time_from,omitempty"`
|
|
TimeTill int64 `json:"time_till,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
}
|
|
|
|
// GetTrends 获取趋势数据
|
|
func (c *Client) GetTrends(ctx context.Context, params *GetTrendsParams) ([]TrendValue, error) {
|
|
if params == nil || len(params.ItemIDs) == 0 {
|
|
return nil, fmt.Errorf("item IDs are required")
|
|
}
|
|
|
|
var result []TrendValue
|
|
if err := c.call(ctx, "trend.get", params, &result); err != nil {
|
|
return nil, fmt.Errorf("get trends failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// ============================================
|
|
// 工具方法
|
|
// ============================================
|
|
|
|
// GetVersion 获取Zabbix服务器版本
|
|
func (c *Client) GetVersion(ctx context.Context) (string, error) {
|
|
var result string
|
|
if err := c.call(ctx, "apiinfo.version", nil, &result); err != nil {
|
|
return "", fmt.Errorf("get version failed: %w", err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetServerTime 获取Zabbix服务器时间
|
|
func (c *Client) GetServerTime(ctx context.Context) (time.Time, error) {
|
|
var result struct {
|
|
TS int64 `json:"ts"`
|
|
}
|
|
if err := c.call(ctx, "script.getscriptsbyevents", map[string]interface{}{}, &result); err != nil {
|
|
// 备选方案:使用配置查询
|
|
return time.Time{}, fmt.Errorf("get server time not directly supported: %w", err)
|
|
}
|
|
return time.Unix(result.TS, 0), nil
|
|
}
|
|
|
|
// ParseTimestamp 解析Zabbix时间戳字符串
|
|
func ParseTimestamp(s string) (time.Time, error) {
|
|
ts, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("parse timestamp failed: %w", err)
|
|
}
|
|
return time.Unix(ts, 0), nil
|
|
}
|
|
|
|
// GetItemValueTypeName 获取值类型名称
|
|
func GetItemValueTypeName(valueType int) string {
|
|
switch valueType {
|
|
case 0:
|
|
return "float"
|
|
case 1:
|
|
return "string"
|
|
case 2:
|
|
return "log"
|
|
case 3:
|
|
return "unsigned"
|
|
case 4:
|
|
return "text"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// GetPriorityName 获取触发器优先级名称
|
|
func GetPriorityName(priority int) string {
|
|
switch priority {
|
|
case 0:
|
|
return "not_classified"
|
|
case 1:
|
|
return "information"
|
|
case 2:
|
|
return "warning"
|
|
case 3:
|
|
return "average"
|
|
case 4:
|
|
return "high"
|
|
case 5:
|
|
return "disaster"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|