package monitor import ( "context" "fmt" "time" "github.com/Fuwn/kaze/internal/config" "github.com/go-ping/ping" ) // ICMPMonitor monitors hosts using ICMP ping type ICMPMonitor struct { id string name string group string target string interval time.Duration timeout time.Duration retries int roundResponseTime bool roundUptime bool count int // Number of ping packets to send } // NewICMPMonitor creates a new ICMP monitor func NewICMPMonitor(cfg config.MonitorConfig) (*ICMPMonitor, error) { // Default to 4 pings if not specified count := 4 if cfg.PingCount > 0 { count = cfg.PingCount } return &ICMPMonitor{ id: cfg.ID(), name: cfg.Name, group: cfg.Group, target: cfg.Target, interval: cfg.Interval.Duration, timeout: cfg.Timeout.Duration, retries: cfg.Retries, roundResponseTime: cfg.RoundResponseTime, roundUptime: cfg.RoundUptime, count: count, }, nil } // ID returns the unique identifier for this monitor func (m *ICMPMonitor) ID() string { return m.id } // Name returns the monitor's name func (m *ICMPMonitor) Name() string { return m.name } // Group returns the group this monitor belongs to func (m *ICMPMonitor) Group() string { return m.group } // Type returns the monitor type func (m *ICMPMonitor) Type() string { return "icmp" } // Target returns the monitor target func (m *ICMPMonitor) Target() string { return m.target } // Interval returns the check interval func (m *ICMPMonitor) Interval() time.Duration { return m.interval } // Retries returns the number of retry attempts func (m *ICMPMonitor) Retries() int { return m.retries } // HideSSLDays returns whether to hide SSL days from display func (m *ICMPMonitor) HideSSLDays() bool { return false // ICMP doesn't use SSL } // RoundResponseTime returns whether to round response time func (m *ICMPMonitor) RoundResponseTime() bool { return m.roundResponseTime } // RoundUptime returns whether to round uptime percentage func (m *ICMPMonitor) RoundUptime() bool { return m.roundUptime } // Check performs the ICMP ping check func (m *ICMPMonitor) Check(ctx context.Context) *Result { result := &Result{ MonitorName: m.id, Timestamp: time.Now(), } pinger, err := ping.NewPinger(m.target) if err != nil { result.Status = StatusDown result.Error = fmt.Errorf("failed to create pinger: %w", err) return result } // Configure pinger pinger.Count = m.count pinger.Timeout = m.timeout pinger.SetPrivileged(false) // Use unprivileged mode (UDP) by default // Run ping err = pinger.Run() if err != nil { result.Status = StatusDown result.Error = fmt.Errorf("ping failed: %w", err) return result } stats := pinger.Statistics() // Check if any packets were received if stats.PacketsRecv == 0 { result.Status = StatusDown result.Error = fmt.Errorf("no response: 0/%d packets received", stats.PacketsSent) return result } // Use average RTT as response time result.ResponseTime = stats.AvgRtt // Check packet loss if stats.PacketLoss > 0 { // Some packet loss - degraded result.Status = StatusDegraded result.Error = fmt.Errorf("%.1f%% packet loss (%d/%d)", stats.PacketLoss, stats.PacketsRecv, stats.PacketsSent) } else { // All packets received - up result.Status = StatusUp } return result }