diff --git a/README.md b/README.md index 2a86a01..4e56263 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ Name | Description -------------------|------------ RADIUS_ADDR | Address of [FreeRADIUS status server](https://wiki.freeradius.org/config/Status). RADIUS_SECRET | FreeRADIUS client secret. +RADIUS_HOMESERVERS | Addresses of home servers separated by comma, e.g. "172.28.1.2:1812,172.28.1.3:1812" diff --git a/client/client.go b/client/client.go index 8a7360c..9a151f1 100644 --- a/client/client.go +++ b/client/client.go @@ -4,16 +4,22 @@ import ( "context" "crypto/hmac" "crypto/md5" + "fmt" + "log" + "net" + "strconv" + "strings" "time" + "github.com/bvantagelimited/freeradius_exporter/freeradius" + "github.com/prometheus/client_golang/prometheus" "layeh.com/radius" "layeh.com/radius/rfc2869" - - "github.com/bvantagelimited/freeradius_exporter/freeradius" ) // Statistics type. type Statistics struct { + Error string Access Access Auth Auth ProxyAccess Access @@ -21,6 +27,25 @@ type Statistics struct { Accounting Accounting ProxyAccounting Accounting Internal Internal + Server Server +} + +// Server specific stats. +type Server struct { + OutstandingRequests uint32 + State uint32 + TimeOfDeath time.Time + TimeOfLife time.Time + LastPacketRecv time.Time + LastPacketSent time.Time + StartTime time.Time + HUPTime time.Time + EmaWindow uint32 + EmaUsecWindow1 uint32 + EmaUsecWindow10 uint32 + QueuePPSIn uint32 + QueuePPSOut uint32 + QueueUsePercentage uint32 } // Access type. @@ -63,21 +88,48 @@ type Internal struct { // FreeRADIUSClient fetches metrics from status server. type FreeRADIUSClient struct { - addr string - packet *radius.Packet - timeout time.Duration + mainAddr string + packets []packetWrapper + timeout time.Duration + metrics map[string]*prometheus.Desc } -// NewFreeRADIUSClient creates an FreeRADIUSClient. -func NewFreeRADIUSClient(addr, secret string, timeout int) (*FreeRADIUSClient, error) { - client := &FreeRADIUSClient{} +type packetWrapper struct { + address string + packet *radius.Packet +} +func newPacket(secret []byte, address string, statAttr radius.Attribute) (*radius.Packet, error) { auth := make([]byte, 16) - hash := hmac.New(md5.New, []byte(secret)) - packet := radius.New(radius.CodeStatusServer, []byte(secret)) + hash := hmac.New(md5.New, secret) + packet := radius.New(radius.CodeStatusServer, secret) rfc2869.MessageAuthenticator_Set(packet, auth) - freeradius.SetValue(packet, freeradius.StatisticsType, radius.NewInteger(uint32(freeradius.StatisticsTypeAll))) + freeradius.SetValue(packet, freeradius.StatisticsType, statAttr) + + host, portStr, err := net.SplitHostPort(address) + if err != nil { + log.Fatalf("failed parsing home server ip ('%v'): %v\n", address, err) + } + portStr = strings.TrimPrefix(portStr, ":") + + ip := net.ParseIP(host) + if ip == nil { + log.Fatalln("ip is nil") + } + + attrIP, err := radius.NewIPAddr(ip) + if err != nil { + log.Fatalln(err) + } + + port, err := strconv.ParseUint(portStr, 10, 32) + if err != nil { + log.Fatalf("failed parsing port ('%v') to uint: %v\n", port, err) + } + + freeradius.SetValue(packet, freeradius.ServerIPAddress, attrIP) + freeradius.SetValue(packet, freeradius.ServerPort, radius.NewInteger(uint32(port))) encode, err := packet.Encode() if err != nil { @@ -87,183 +139,442 @@ func NewFreeRADIUSClient(addr, secret string, timeout int) (*FreeRADIUSClient, e hash.Write(encode) rfc2869.MessageAuthenticator_Set(packet, hash.Sum(nil)) - client.addr = addr - client.packet = packet + return packet, err +} + +// NewFreeRADIUSClient creates an FreeRADIUSClient. +func NewFreeRADIUSClient(addr string, homeServers []string, secret string, timeout int) (*FreeRADIUSClient, error) { + client := &FreeRADIUSClient{} + client.mainAddr = addr client.timeout = time.Duration(timeout) * time.Millisecond + client.metrics = metrics + packet, err := newPacket([]byte(secret), addr, radius.NewInteger(uint32(freeradius.StatisticsTypeAll))) + if err != nil { + log.Fatalf("failed creating new packet for address '%v'\n", addr) + } + client.packets = append(client.packets, packetWrapper{packet: packet, address: addr}) + + // add home server stats + for _, hs := range homeServers { + if hs == "" { + continue + } + packet, err := newPacket([]byte(secret), hs, radius.NewInteger(uint32( + freeradius.StatisticsTypeAuthentication| // will give "Home server is not auth" stats error when server is acct (but won't fail and give the available metrics) + freeradius.StatisticsTypeAccounting| // will give "Home server is not acct" stats error when server is auth (but won't fail and give the available metrics) + freeradius.StatisticsTypeInternal| + freeradius.StatisticsTypeHomeServer, + ))) + if err != nil { + log.Fatalf("failed creating new packet for address '%v'\n", addr) + } + client.packets = append(client.packets, packetWrapper{packet: packet, address: hs}) + } return client, nil } // Stats fetches statistics. -func (f *FreeRADIUSClient) Stats() (Statistics, error) { - stats := Statistics{} +func (f *FreeRADIUSClient) Stats() ([]prometheus.Metric, error) { + var allStats []prometheus.Metric ctx, cancel := context.WithTimeout(context.Background(), f.timeout) defer cancel() - response, err := radius.Exchange(ctx, f.packet, f.addr) - if err != nil { - return stats, err - } + for _, p := range f.packets { + stats := Statistics{} - if response.Code == radius.CodeAccessAccept { - stats.Access.Requests, err = freeradius.GetValue(response, freeradius.TotalAccessRequests) + response, err := radius.Exchange(ctx, p.packet, f.mainAddr) if err != nil { - return stats, err + return nil, fmt.Errorf("exchange failed: %w", err) + } - stats.Access.Accepts, err = freeradius.GetValue(response, freeradius.TotalAccessAccepts) - if err != nil { - return stats, err + + if response.Code != radius.CodeAccessAccept { + return nil, fmt.Errorf("got response code '%v'", response.Code) } - stats.Access.Rejects, err = freeradius.GetValue(response, freeradius.TotalAccessRejects) - if err != nil { - return stats, err + + statsErr, err := freeradius.GetString(response, freeradius.StatsError) + if err == nil { // when there is no lookup error for this attribute, there is a freeradius-stats-error + log.Printf("error form stats server (main %v or home server: %v): '%v'", f.mainAddr, p.address, statsErr) } - stats.Access.Challenges, err = freeradius.GetValue(response, freeradius.TotalAccessChallenges) - if err != nil { - return stats, err + + stats.Error = statsErr + m := prometheus.MustNewConstMetric(f.metrics["freeradius_stats_error"], prometheus.GaugeValue, 1, stats.Error, p.address) + allStats = append(allStats, m) + + if stats.Server.LastPacketRecv, err = freeradius.GetDate(response, freeradius.LastPacketRecv); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_last_packet_recv"], prometheus.GaugeValue, float64(stats.Server.LastPacketRecv.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.Responses, err = freeradius.GetValue(response, freeradius.TotalAuthResponses) - if err != nil { - return stats, err + if stats.Server.LastPacketSent, err = freeradius.GetDate(response, freeradius.LastPacketSent); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_last_packet_sent"], prometheus.GaugeValue, float64(stats.Server.LastPacketSent.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.DuplicateRequests, err = freeradius.GetValue(response, freeradius.TotalAuthDuplicateRequests) - if err != nil { - return stats, err + + if stats.Server.HUPTime, err = freeradius.GetDate(response, freeradius.HUPTime); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_hup_time"], prometheus.GaugeValue, float64(stats.Server.HUPTime.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.MalformedRequests, err = freeradius.GetValue(response, freeradius.TotalAuthMalformedRequests) - if err != nil { - return stats, err + if stats.Server.StartTime, err = freeradius.GetDate(response, freeradius.StartTime); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_start_time"], prometheus.GaugeValue, float64(stats.Server.StartTime.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.InvalidRequests, err = freeradius.GetValue(response, freeradius.TotalAuthInvalidRequests) - if err != nil { - return stats, err + + if stats.Server.State, err = freeradius.GetInt(response, freeradius.ServerState); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_state"], prometheus.GaugeValue, float64(stats.Server.State), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.DroppedRequests, err = freeradius.GetValue(response, freeradius.TotalAuthDroppedRequests) - if err != nil { - return stats, err + + if stats.Server.TimeOfDeath, err = freeradius.GetDate(response, freeradius.ServerTimeOfDeath); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_time_of_death"], prometheus.GaugeValue, float64(stats.Server.TimeOfDeath.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Auth.UnknownTypes, err = freeradius.GetValue(response, freeradius.TotalAuthUnknownTypes) - if err != nil { - return stats, err + if stats.Server.TimeOfLife, err = freeradius.GetDate(response, freeradius.ServerTimeOfLife); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_time_of_life"], prometheus.GaugeValue, float64(stats.Server.TimeOfLife.Unix()), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccess.Requests, err = freeradius.GetValue(response, freeradius.TotalProxyAccessRequests) - if err != nil { - return stats, err + + if stats.Server.EmaWindow, err = freeradius.GetInt(response, freeradius.EmaWindow); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_ema_window"], prometheus.GaugeValue, float64(stats.Server.EmaWindow), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccess.Accepts, err = freeradius.GetValue(response, freeradius.TotalProxyAccessAccepts) - if err != nil { - return stats, err + + if stats.Server.EmaUsecWindow1, err = freeradius.GetInt(response, freeradius.EmaUsecWindow1); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_ema_window1_usec"], prometheus.GaugeValue, float64(stats.Server.EmaUsecWindow1), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccess.Rejects, err = freeradius.GetValue(response, freeradius.TotalProxyAccessRejects) - if err != nil { - return stats, err + + if stats.Server.EmaUsecWindow10, err = freeradius.GetInt(response, freeradius.EmaUsecWindow10); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_ema_window10_usec"], prometheus.GaugeValue, float64(stats.Server.EmaUsecWindow10), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccess.Challenges, err = freeradius.GetValue(response, freeradius.TotalProxyAccessChallenges) - if err != nil { - return stats, err + + if stats.Server.OutstandingRequests, err = freeradius.GetInt(response, freeradius.ServerOutstandingRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_outstanding_requests"], prometheus.GaugeValue, float64(stats.Server.OutstandingRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.Responses, err = freeradius.GetValue(response, freeradius.TotalProxyAuthResponses) - if err != nil { - return stats, err + + if stats.Server.QueuePPSIn, err = freeradius.GetInt(response, freeradius.QueuePPSIn); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_pps_in"], prometheus.GaugeValue, float64(stats.Server.QueuePPSIn), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.DuplicateRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAuthDuplicateRequests) - if err != nil { - return stats, err + + if stats.Server.QueuePPSOut, err = freeradius.GetInt(response, freeradius.QueuePPSOut); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_pps_out"], prometheus.GaugeValue, float64(stats.Server.QueuePPSOut), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.MalformedRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAuthMalformedRequests) - if err != nil { - return stats, err + + if stats.Server.QueueUsePercentage, err = freeradius.GetInt(response, freeradius.QueueUsePercentage); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_use_percentage"], prometheus.GaugeValue, float64(stats.Server.QueuePPSOut), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.InvalidRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAuthInvalidRequests) - if err != nil { - return stats, err + + if stats.Access.Requests, err = freeradius.GetInt(response, freeradius.TotalAccessRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_requests"], prometheus.CounterValue, float64(stats.Access.Requests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.DroppedRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAuthDroppedRequests) - if err != nil { - return stats, err + + if stats.Access.Accepts, err = freeradius.GetInt(response, freeradius.TotalAccessAccepts); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_accepts"], prometheus.CounterValue, float64(stats.Access.Accepts), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAuth.UnknownTypes, err = freeradius.GetValue(response, freeradius.TotalProxyAuthUnknownTypes) - if err != nil { - return stats, err + + if stats.Access.Rejects, err = freeradius.GetInt(response, freeradius.TotalAccessRejects); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_rejects"], prometheus.CounterValue, float64(stats.Access.Rejects), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.Requests, err = freeradius.GetValue(response, freeradius.TotalAccountingRequests) - if err != nil { - return stats, err + + if stats.Access.Challenges, err = freeradius.GetInt(response, freeradius.TotalAccessChallenges); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_challenges"], prometheus.CounterValue, float64(stats.Access.Challenges), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.Responses, err = freeradius.GetValue(response, freeradius.TotalAccountingResponses) - if err != nil { - return stats, err + + if stats.Auth.Responses, err = freeradius.GetInt(response, freeradius.TotalAuthResponses); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_responses"], prometheus.CounterValue, float64(stats.Auth.Responses), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.DuplicateRequests, err = freeradius.GetValue(response, freeradius.TotalAcctDuplicateRequests) - if err != nil { - return stats, err + + if stats.Auth.DuplicateRequests, err = freeradius.GetInt(response, freeradius.TotalAuthDuplicateRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_duplicate_requests"], prometheus.CounterValue, float64(stats.Auth.DuplicateRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.MalformedRequests, err = freeradius.GetValue(response, freeradius.TotalAcctMalformedRequests) - if err != nil { - return stats, err + + if stats.Auth.MalformedRequests, err = freeradius.GetInt(response, freeradius.TotalAuthMalformedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_malformed_requests"], prometheus.CounterValue, float64(stats.Auth.MalformedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.InvalidRequests, err = freeradius.GetValue(response, freeradius.TotalAcctInvalidRequests) - if err != nil { - return stats, err + + if stats.Auth.InvalidRequests, err = freeradius.GetInt(response, freeradius.TotalAuthInvalidRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_invalid_requests"], prometheus.CounterValue, float64(stats.Auth.InvalidRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.DroppedRequests, err = freeradius.GetValue(response, freeradius.TotalAcctDroppedRequests) - if err != nil { - return stats, err + + if stats.Auth.DroppedRequests, err = freeradius.GetInt(response, freeradius.TotalAuthDroppedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_dropped_requests"], prometheus.CounterValue, float64(stats.Auth.DroppedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Accounting.UnknownTypes, err = freeradius.GetValue(response, freeradius.TotalAcctUnknownTypes) - if err != nil { - return stats, err + + if stats.Auth.UnknownTypes, err = freeradius.GetInt(response, freeradius.TotalAuthUnknownTypes); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_unknown_types"], prometheus.CounterValue, float64(stats.Auth.UnknownTypes), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.Requests, err = freeradius.GetValue(response, freeradius.TotalProxyAccountingRequests) - if err != nil { - return stats, err + + if stats.ProxyAccess.Requests, err = freeradius.GetInt(response, freeradius.TotalProxyAccessRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_requests"], prometheus.CounterValue, float64(stats.ProxyAccess.Requests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.Responses, err = freeradius.GetValue(response, freeradius.TotalProxyAccountingResponses) - if err != nil { - return stats, err + + if stats.ProxyAccess.Accepts, err = freeradius.GetInt(response, freeradius.TotalProxyAccessAccepts); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_accepts"], prometheus.CounterValue, float64(stats.ProxyAccess.Accepts), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.DuplicateRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAcctDuplicateRequests) - if err != nil { - return stats, err + + if stats.ProxyAccess.Rejects, err = freeradius.GetInt(response, freeradius.TotalProxyAccessRejects); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_rejects"], prometheus.CounterValue, float64(stats.ProxyAccess.Rejects), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.MalformedRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAcctMalformedRequests) - if err != nil { - return stats, err + + if stats.ProxyAccess.Challenges, err = freeradius.GetInt(response, freeradius.TotalProxyAccessChallenges); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_challenges"], prometheus.CounterValue, float64(stats.ProxyAccess.Challenges), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.InvalidRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAcctInvalidRequests) - if err != nil { - return stats, err + + if stats.ProxyAuth.Responses, err = freeradius.GetInt(response, freeradius.TotalProxyAuthResponses); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_responses"], prometheus.CounterValue, float64(stats.ProxyAuth.Responses), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.DroppedRequests, err = freeradius.GetValue(response, freeradius.TotalProxyAcctDroppedRequests) - if err != nil { - return stats, err + + if stats.ProxyAuth.DuplicateRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAuthDuplicateRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_duplicate_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.DuplicateRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.ProxyAccounting.UnknownTypes, err = freeradius.GetValue(response, freeradius.TotalProxyAcctUnknownTypes) - if err != nil { - return stats, err + + if stats.ProxyAuth.MalformedRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAuthMalformedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_malformed_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.MalformedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Internal.QueueLenInternal, err = freeradius.GetValue(response, freeradius.QueueLenInternal) - if err != nil { - return stats, err + + if stats.ProxyAuth.InvalidRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAuthInvalidRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_invalid_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.InvalidRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Internal.QueueLenProxy, err = freeradius.GetValue(response, freeradius.QueueLenProxy) - if err != nil { - return stats, err + + if stats.ProxyAuth.DroppedRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAuthDroppedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_dropped_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.DroppedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Internal.QueueLenAuth, err = freeradius.GetValue(response, freeradius.QueueLenAuth) - if err != nil { - return stats, err + + if stats.ProxyAuth.UnknownTypes, err = freeradius.GetInt(response, freeradius.TotalProxyAuthUnknownTypes); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_unknown_types"], prometheus.CounterValue, float64(stats.ProxyAuth.UnknownTypes), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Internal.QueueLenAcct, err = freeradius.GetValue(response, freeradius.QueueLenAcct) - if err != nil { - return stats, err + + if stats.Accounting.Requests, err = freeradius.GetInt(response, freeradius.TotalAccountingRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_requests"], prometheus.CounterValue, float64(stats.Accounting.Requests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } - stats.Internal.QueueLenDetail, err = freeradius.GetValue(response, freeradius.QueueLenDetail) - if err != nil { - return stats, err + + if stats.Accounting.Responses, err = freeradius.GetInt(response, freeradius.TotalAccountingResponses); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_responses"], prometheus.CounterValue, float64(stats.Accounting.Responses), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Accounting.DuplicateRequests, err = freeradius.GetInt(response, freeradius.TotalAcctDuplicateRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_duplicate_requests"], prometheus.CounterValue, float64(stats.Accounting.DuplicateRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Accounting.MalformedRequests, err = freeradius.GetInt(response, freeradius.TotalAcctMalformedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_malformed_requests"], prometheus.CounterValue, float64(stats.Accounting.MalformedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Accounting.InvalidRequests, err = freeradius.GetInt(response, freeradius.TotalAcctInvalidRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_invalid_requests"], prometheus.CounterValue, float64(stats.Accounting.InvalidRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Accounting.DroppedRequests, err = freeradius.GetInt(response, freeradius.TotalAcctDroppedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_dropped_requests"], prometheus.CounterValue, float64(stats.Accounting.DroppedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Accounting.UnknownTypes, err = freeradius.GetInt(response, freeradius.TotalAcctUnknownTypes); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_unknown_types"], prometheus.CounterValue, float64(stats.Accounting.UnknownTypes), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.Requests, err = freeradius.GetInt(response, freeradius.TotalProxyAccountingRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.Requests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.Responses, err = freeradius.GetInt(response, freeradius.TotalProxyAccountingResponses); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_responses"], prometheus.CounterValue, float64(stats.ProxyAccounting.Responses), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.DuplicateRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAcctDuplicateRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_duplicate_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.DuplicateRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.MalformedRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAcctMalformedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_malformed_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.MalformedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.InvalidRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAcctInvalidRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_invalid_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.InvalidRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.DroppedRequests, err = freeradius.GetInt(response, freeradius.TotalProxyAcctDroppedRequests); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_dropped_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.DroppedRequests), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.ProxyAccounting.UnknownTypes, err = freeradius.GetInt(response, freeradius.TotalProxyAcctUnknownTypes); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_unknown_types"], prometheus.CounterValue, float64(stats.ProxyAccounting.UnknownTypes), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Internal.QueueLenInternal, err = freeradius.GetInt(response, freeradius.QueueLenInternal); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_internal"], prometheus.GaugeValue, float64(stats.Internal.QueueLenInternal), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Internal.QueueLenProxy, err = freeradius.GetInt(response, freeradius.QueueLenProxy); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_proxy"], prometheus.GaugeValue, float64(stats.Internal.QueueLenProxy), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Internal.QueueLenAuth, err = freeradius.GetInt(response, freeradius.QueueLenAuth); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_auth"], prometheus.GaugeValue, float64(stats.Internal.QueueLenAuth), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Internal.QueueLenAcct, err = freeradius.GetInt(response, freeradius.QueueLenAcct); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_acct"], prometheus.GaugeValue, float64(stats.Internal.QueueLenAcct), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) + } + + if stats.Internal.QueueLenDetail, err = freeradius.GetInt(response, freeradius.QueueLenDetail); err == nil { + allStats = append(allStats, prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_detail"], prometheus.GaugeValue, float64(stats.Internal.QueueLenDetail), p.address)) + } else if err != nil && err != radius.ErrNoAttribute { + log.Println(err) } } - return stats, nil + return allStats, nil +} + +var metrics = map[string]*prometheus.Desc{ + "freeradius_total_access_requests": prometheus.NewDesc("freeradius_total_access_requests", "Total access requests", []string{"address"}, nil), + "freeradius_total_access_accepts": prometheus.NewDesc("freeradius_total_access_accepts", "Total access accepts", []string{"address"}, nil), + "freeradius_total_access_rejects": prometheus.NewDesc("freeradius_total_access_rejects", "Total access rejects", []string{"address"}, nil), + "freeradius_total_access_challenges": prometheus.NewDesc("freeradius_total_access_challenges", "Total access challenges", []string{"address"}, nil), + "freeradius_total_auth_responses": prometheus.NewDesc("freeradius_total_auth_responses", "Total auth responses", []string{"address"}, nil), + "freeradius_total_auth_duplicate_requests": prometheus.NewDesc("freeradius_total_auth_duplicate_requests", "Total auth duplicate requests", []string{"address"}, nil), + "freeradius_total_auth_malformed_requests": prometheus.NewDesc("freeradius_total_auth_malformed_requests", "Total auth malformed requests", []string{"address"}, nil), + "freeradius_total_auth_invalid_requests": prometheus.NewDesc("freeradius_total_auth_invalid_requests", "Total auth invalid requests", []string{"address"}, nil), + "freeradius_total_auth_dropped_requests": prometheus.NewDesc("freeradius_total_auth_dropped_requests", "Total auth dropped requests", []string{"address"}, nil), + "freeradius_total_auth_unknown_types": prometheus.NewDesc("freeradius_total_auth_unknown_types", "Total auth unknown types", []string{"address"}, nil), + "freeradius_total_proxy_access_requests": prometheus.NewDesc("freeradius_total_proxy_access_requests", "Total proxy access requests", []string{"address"}, nil), + "freeradius_total_proxy_access_accepts": prometheus.NewDesc("freeradius_total_proxy_access_accepts", "Total proxy access accepts", []string{"address"}, nil), + "freeradius_total_proxy_access_rejects": prometheus.NewDesc("freeradius_total_proxy_access_rejects", "Total proxy access rejects", []string{"address"}, nil), + "freeradius_total_proxy_access_challenges": prometheus.NewDesc("freeradius_total_proxy_access_challenges", "Total proxy access challenges", []string{"address"}, nil), + "freeradius_total_proxy_auth_responses": prometheus.NewDesc("freeradius_total_proxy_auth_responses", "Total proxy auth responses", []string{"address"}, nil), + "freeradius_total_proxy_auth_duplicate_requests": prometheus.NewDesc("freeradius_total_proxy_auth_duplicate_requests", "Total proxy auth duplicate requests", []string{"address"}, nil), + "freeradius_total_proxy_auth_malformed_requests": prometheus.NewDesc("freeradius_total_proxy_auth_malformed_requests", "Total proxy auth malformed requests", []string{"address"}, nil), + "freeradius_total_proxy_auth_invalid_requests": prometheus.NewDesc("freeradius_total_proxy_auth_invalid_requests", "Total proxy auth invalid requests", []string{"address"}, nil), + "freeradius_total_proxy_auth_dropped_requests": prometheus.NewDesc("freeradius_total_proxy_auth_dropped_requests", "Total proxy auth dropped requests", []string{"address"}, nil), + "freeradius_total_proxy_auth_unknown_types": prometheus.NewDesc("freeradius_total_proxy_auth_unknown_types", "Total proxy auth unknown types", []string{"address"}, nil), + "freeradius_total_acct_requests": prometheus.NewDesc("freeradius_total_acct_requests", "Total acct requests", []string{"address"}, nil), + "freeradius_total_acct_responses": prometheus.NewDesc("freeradius_total_acct_responses", "Total acct responses", []string{"address"}, nil), + "freeradius_total_acct_duplicate_requests": prometheus.NewDesc("freeradius_total_acct_duplicate_requests", "Total acct duplicate requests", []string{"address"}, nil), + "freeradius_total_acct_malformed_requests": prometheus.NewDesc("freeradius_total_acct_malformed_requests", "Total acct malformed requests", []string{"address"}, nil), + "freeradius_total_acct_invalid_requests": prometheus.NewDesc("freeradius_total_acct_invalid_requests", "Total acct invalid requests", []string{"address"}, nil), + "freeradius_total_acct_dropped_requests": prometheus.NewDesc("freeradius_total_acct_dropped_requests", "Total acct dropped requests", []string{"address"}, nil), + "freeradius_total_acct_unknown_types": prometheus.NewDesc("freeradius_total_acct_unknown_types", "Total acct unknown types", []string{"address"}, nil), + "freeradius_total_proxy_acct_requests": prometheus.NewDesc("freeradius_total_proxy_acct_requests", "Total proxy acct requests", []string{"address"}, nil), + "freeradius_total_proxy_acct_responses": prometheus.NewDesc("freeradius_total_proxy_acct_responses", "Total proxy acct responses", []string{"address"}, nil), + "freeradius_total_proxy_acct_duplicate_requests": prometheus.NewDesc("freeradius_total_proxy_acct_duplicate_requests", "Total proxy acct duplicate requests", []string{"address"}, nil), + "freeradius_total_proxy_acct_malformed_requests": prometheus.NewDesc("freeradius_total_proxy_acct_malformed_requests", "Total proxy acct malformed requests", []string{"address"}, nil), + "freeradius_total_proxy_acct_invalid_requests": prometheus.NewDesc("freeradius_total_proxy_acct_invalid_requests", "Total proxy acct invalid requests", []string{"address"}, nil), + "freeradius_total_proxy_acct_dropped_requests": prometheus.NewDesc("freeradius_total_proxy_acct_dropped_requests", "Total proxy acct dropped requests", []string{"address"}, nil), + "freeradius_total_proxy_acct_unknown_types": prometheus.NewDesc("freeradius_total_proxy_acct_unknown_types", "Total proxy acct unknown types", []string{"address"}, nil), + "freeradius_queue_len_internal": prometheus.NewDesc("freeradius_queue_len_internal", "Interal queue length", []string{"address"}, nil), + "freeradius_queue_len_proxy": prometheus.NewDesc("freeradius_queue_len_proxy", "Proxy queue length", []string{"address"}, nil), + "freeradius_queue_len_auth": prometheus.NewDesc("freeradius_queue_len_auth", "Auth queue length", []string{"address"}, nil), + "freeradius_queue_len_acct": prometheus.NewDesc("freeradius_queue_len_acct", "Acct queue length", []string{"address"}, nil), + "freeradius_queue_len_detail": prometheus.NewDesc("freeradius_queue_len_detail", "Detail queue length", []string{"address"}, nil), + "freeradius_last_packet_recv": prometheus.NewDesc("freeradius_last_packet_recv", "Epoch timestamp when the last packet was received", []string{"address"}, nil), + "freeradius_last_packet_sent": prometheus.NewDesc("freeradius_last_packet_sent", "Epoch timestamp when the last packet was sent", []string{"address"}, nil), + "freeradius_start_time": prometheus.NewDesc("freeradius_start_time", "Epoch timestamp when the server was started", []string{"address"}, nil), + "freeradius_hup_time": prometheus.NewDesc("freeradius_hup_time", "Epoch timestamp when the server hang up (If start == hup, it hasn't been hup'd yet)", []string{"address"}, nil), + "freeradius_state": prometheus.NewDesc("freeradius_state", "State of the server. Alive = 0; Zombie = 1; Dead = 2; Idle = 3", []string{"address"}, nil), + "freeradius_time_of_death": prometheus.NewDesc("freeradius_time_of_death", "Epoch timestamp when a home server is marked as 'dead'", []string{"address"}, nil), + "freeradius_time_of_life": prometheus.NewDesc("freeradius_time_of_life", "Epoch timestamp when a home server is marked as 'alive'", []string{"address"}, nil), + "freeradius_ema_window": prometheus.NewDesc("freeradius_ema_window", "Exponential moving average of home server response time", []string{"address"}, nil), + "freeradius_ema_window1_usec": prometheus.NewDesc("freeradius_ema_window1_usec", "Window-1 is the average is calculated over 'window' packets", []string{"address"}, nil), + "freeradius_ema_window10_usec": prometheus.NewDesc("freeradius_ema_window10_usec", "Window-10 is the average is calculated over '10 * window' packets", []string{"address"}, nil), + "freeradius_outstanding_requests": prometheus.NewDesc("freeradius_outstanding_requests", "Outstanding requests", []string{"address"}, nil), + "freeradius_queue_pps_in": prometheus.NewDesc("freeradius_queue_pps_in", "Queue PPS in", []string{"address"}, nil), + "freeradius_queue_pps_out": prometheus.NewDesc("freeradius_queue_pps_out", "Queue PPS out", []string{"address"}, nil), + "freeradius_queue_use_percentage": prometheus.NewDesc("freeradius_queue_use_percentage", "Queue usage percentage", []string{"address"}, nil), + "freeradius_stats_error": prometheus.NewDesc("freeradius_stats_error", "Stats error as label with a const value of 1", []string{"error", "address"}, nil), } diff --git a/collector/collector.go b/collector/collector.go index 1db42af..744504f 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -4,15 +4,13 @@ import ( "log" "sync" - "github.com/prometheus/client_golang/prometheus" - "github.com/bvantagelimited/freeradius_exporter/client" + "github.com/prometheus/client_golang/prometheus" ) // FreeRADIUSCollector type. type FreeRADIUSCollector struct { - client *client.FreeRADIUSClient - metrics map[string]*prometheus.Desc + client *client.FreeRADIUSClient // indicates if we could reach freeradius or not up *prometheus.Desc mutex sync.Mutex @@ -23,56 +21,13 @@ func NewFreeRADIUSCollector(cl *client.FreeRADIUSClient) *FreeRADIUSCollector { return &FreeRADIUSCollector{ client: cl, up: prometheus.NewDesc( - "freeradius_up", "Boolean gauge of 1 if freeradius was reachable, or 0 if not", nil, nil), - metrics: map[string]*prometheus.Desc{ - "freeradius_total_access_requests": prometheus.NewDesc("freeradius_total_access_requests", "Total access requests", nil, nil), - "freeradius_total_access_accepts": prometheus.NewDesc("freeradius_total_access_accepts", "Total access accepts", nil, nil), - "freeradius_total_access_rejects": prometheus.NewDesc("freeradius_total_access_rejects", "Total access rejects", nil, nil), - "freeradius_total_access_challenges": prometheus.NewDesc("freeradius_total_access_challenges", "Total access challenges", nil, nil), - "freeradius_total_auth_responses": prometheus.NewDesc("freeradius_total_auth_responses", "Total auth responses", nil, nil), - "freeradius_total_auth_duplicate_requests": prometheus.NewDesc("freeradius_total_auth_duplicate_requests", "Total auth duplicate requests", nil, nil), - "freeradius_total_auth_malformed_requests": prometheus.NewDesc("freeradius_total_auth_malformed_requests", "Total auth malformed requests", nil, nil), - "freeradius_total_auth_invalid_requests": prometheus.NewDesc("freeradius_total_auth_invalid_requests", "Total auth invalid requests", nil, nil), - "freeradius_total_auth_dropped_requests": prometheus.NewDesc("freeradius_total_auth_dropped_requests", "Total auth dropped requests", nil, nil), - "freeradius_total_auth_unknown_types": prometheus.NewDesc("freeradius_total_auth_unknown_types", "Total auth unknown types", nil, nil), - "freeradius_total_proxy_access_requests": prometheus.NewDesc("freeradius_total_proxy_access_requests", "Total proxy access requests", nil, nil), - "freeradius_total_proxy_access_accepts": prometheus.NewDesc("freeradius_total_proxy_access_accepts", "Total proxy access accepts", nil, nil), - "freeradius_total_proxy_access_rejects": prometheus.NewDesc("freeradius_total_proxy_access_rejects", "Total proxy access rejects", nil, nil), - "freeradius_total_proxy_access_challenges": prometheus.NewDesc("freeradius_total_proxy_access_challenges", "Total proxy access challenges", nil, nil), - "freeradius_total_proxy_auth_responses": prometheus.NewDesc("freeradius_total_proxy_auth_responses", "Total proxy auth responses", nil, nil), - "freeradius_total_proxy_auth_duplicate_requests": prometheus.NewDesc("freeradius_total_proxy_auth_duplicate_requests", "Total proxy auth duplicate requests", nil, nil), - "freeradius_total_proxy_auth_malformed_requests": prometheus.NewDesc("freeradius_total_proxy_auth_malformed_requests", "Total proxy auth malformed requests", nil, nil), - "freeradius_total_proxy_auth_invalid_requests": prometheus.NewDesc("freeradius_total_proxy_auth_invalid_requests", "Total proxy auth invalid requests", nil, nil), - "freeradius_total_proxy_auth_dropped_requests": prometheus.NewDesc("freeradius_total_proxy_auth_dropped_requests", "Total proxy auth dropped requests", nil, nil), - "freeradius_total_proxy_auth_unknown_types": prometheus.NewDesc("freeradius_total_proxy_auth_unknown_types", "Total proxy auth unknown types", nil, nil), - "freeradius_total_acct_requests": prometheus.NewDesc("freeradius_total_acct_requests", "Total acct requests", nil, nil), - "freeradius_total_acct_responses": prometheus.NewDesc("freeradius_total_acct_responses", "Total acct responses", nil, nil), - "freeradius_total_acct_duplicate_requests": prometheus.NewDesc("freeradius_total_acct_duplicate_requests", "Total acct duplicate requests", nil, nil), - "freeradius_total_acct_malformed_requests": prometheus.NewDesc("freeradius_total_acct_malformed_requests", "Total acct malformed requests", nil, nil), - "freeradius_total_acct_invalid_requests": prometheus.NewDesc("freeradius_total_acct_invalid_requests", "Total acct invalid requests", nil, nil), - "freeradius_total_acct_dropped_requests": prometheus.NewDesc("freeradius_total_acct_dropped_requests", "Total acct dropped requests", nil, nil), - "freeradius_total_acct_unknown_types": prometheus.NewDesc("freeradius_total_acct_unknown_types", "Total acct unknown types", nil, nil), - "freeradius_total_proxy_acct_requests": prometheus.NewDesc("freeradius_total_proxy_acct_requests", "Total proxy acct requests", nil, nil), - "freeradius_total_proxy_acct_responses": prometheus.NewDesc("freeradius_total_proxy_acct_responses", "Total proxy acct responses", nil, nil), - "freeradius_total_proxy_acct_duplicate_requests": prometheus.NewDesc("freeradius_total_proxy_acct_duplicate_requests", "Total proxy acct duplicate requests", nil, nil), - "freeradius_total_proxy_acct_malformed_requests": prometheus.NewDesc("freeradius_total_proxy_acct_malformed_requests", "Total proxy acct malformed requests", nil, nil), - "freeradius_total_proxy_acct_invalid_requests": prometheus.NewDesc("freeradius_total_proxy_acct_invalid_requests", "Total proxy acct invalid requests", nil, nil), - "freeradius_total_proxy_acct_dropped_requests": prometheus.NewDesc("freeradius_total_proxy_acct_dropped_requests", "Total proxy acct dropped requests", nil, nil), - "freeradius_total_proxy_acct_unknown_types": prometheus.NewDesc("freeradius_total_proxy_acct_unknown_types", "Total proxy acct unknown types", nil, nil), - "freeradius_queue_len_internal": prometheus.NewDesc("freeradius_queue_len_internal", "Interal queue length", nil, nil), - "freeradius_queue_len_proxy": prometheus.NewDesc("freeradius_queue_len_proxy", "Proxy queue length", nil, nil), - "freeradius_queue_len_auth": prometheus.NewDesc("freeradius_queue_len_auth", "Auth queue length", nil, nil), - "freeradius_queue_len_acct": prometheus.NewDesc("freeradius_queue_len_acct", "Acct queue length", nil, nil), - "freeradius_queue_len_detail": prometheus.NewDesc("freeradius_queue_len_detail", "Detail queue length", nil, nil), - }, + "freeradius_up", "Boolean gauge of 1 if freeradius was reachable, or 0 if not", []string{}, nil), } } // Describe outputs metrics descriptions. func (f *FreeRADIUSCollector) Describe(ch chan<- *prometheus.Desc) { - for _, m := range f.metrics { - ch <- m - } + // nothing } // Collect fetches metrics from and sends them to the provided channel. @@ -80,51 +35,15 @@ func (f *FreeRADIUSCollector) Collect(ch chan<- prometheus.Metric) { f.mutex.Lock() defer f.mutex.Unlock() - stats, err := f.client.Stats() + allStats, err := f.client.Stats() if err != nil { + log.Println(err) ch <- prometheus.MustNewConstMetric(f.up, prometheus.GaugeValue, float64(0)) - log.Printf("Error fetching stats: %v", err) return } ch <- prometheus.MustNewConstMetric(f.up, prometheus.GaugeValue, float64(1)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_requests"], prometheus.CounterValue, float64(stats.Access.Requests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_accepts"], prometheus.CounterValue, float64(stats.Access.Accepts)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_rejects"], prometheus.CounterValue, float64(stats.Access.Rejects)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_access_challenges"], prometheus.CounterValue, float64(stats.Access.Challenges)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_responses"], prometheus.CounterValue, float64(stats.Auth.Responses)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_duplicate_requests"], prometheus.CounterValue, float64(stats.Auth.DuplicateRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_malformed_requests"], prometheus.CounterValue, float64(stats.Auth.MalformedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_invalid_requests"], prometheus.CounterValue, float64(stats.Auth.InvalidRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_dropped_requests"], prometheus.CounterValue, float64(stats.Auth.DroppedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_auth_unknown_types"], prometheus.CounterValue, float64(stats.Auth.UnknownTypes)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_requests"], prometheus.CounterValue, float64(stats.ProxyAccess.Requests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_accepts"], prometheus.CounterValue, float64(stats.ProxyAccess.Accepts)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_rejects"], prometheus.CounterValue, float64(stats.ProxyAccess.Rejects)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_access_challenges"], prometheus.CounterValue, float64(stats.ProxyAccess.Challenges)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_responses"], prometheus.CounterValue, float64(stats.ProxyAuth.Responses)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_duplicate_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.DuplicateRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_malformed_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.MalformedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_invalid_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.InvalidRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_dropped_requests"], prometheus.CounterValue, float64(stats.ProxyAuth.DroppedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_auth_unknown_types"], prometheus.CounterValue, float64(stats.ProxyAuth.UnknownTypes)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_requests"], prometheus.CounterValue, float64(stats.Accounting.Requests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_responses"], prometheus.CounterValue, float64(stats.Accounting.Responses)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_duplicate_requests"], prometheus.CounterValue, float64(stats.Accounting.DuplicateRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_malformed_requests"], prometheus.CounterValue, float64(stats.Accounting.MalformedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_invalid_requests"], prometheus.CounterValue, float64(stats.Accounting.InvalidRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_dropped_requests"], prometheus.CounterValue, float64(stats.Accounting.DroppedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_acct_unknown_types"], prometheus.CounterValue, float64(stats.Accounting.UnknownTypes)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.Requests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_responses"], prometheus.CounterValue, float64(stats.ProxyAccounting.Responses)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_duplicate_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.DuplicateRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_malformed_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.MalformedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_invalid_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.InvalidRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_dropped_requests"], prometheus.CounterValue, float64(stats.ProxyAccounting.DroppedRequests)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_total_proxy_acct_unknown_types"], prometheus.CounterValue, float64(stats.ProxyAccounting.UnknownTypes)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_internal"], prometheus.GaugeValue, float64(stats.Internal.QueueLenInternal)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_proxy"], prometheus.GaugeValue, float64(stats.Internal.QueueLenProxy)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_auth"], prometheus.GaugeValue, float64(stats.Internal.QueueLenAuth)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_acct"], prometheus.GaugeValue, float64(stats.Internal.QueueLenAcct)) - ch <- prometheus.MustNewConstMetric(f.metrics["freeradius_queue_len_detail"], prometheus.GaugeValue, float64(stats.Internal.QueueLenDetail)) + for _, stats := range allStats { + ch <- stats + } } diff --git a/freeradius/freeradius.go b/freeradius/freeradius.go index a55cb17..65b2810 100644 --- a/freeradius/freeradius.go +++ b/freeradius/freeradius.go @@ -1,6 +1,8 @@ package freeradius import ( + "time" + "layeh.com/radius" "layeh.com/radius/rfc2865" ) @@ -74,10 +76,35 @@ const ( QueueLenAuth = 164 QueueLenAcct = 165 QueueLenDetail = 166 + + ServerIPAddress = 170 // ipaddr + ServerPort = 171 // integer + ServerOutstandingRequests = 172 // integer + + // Alive 0; Zombie 1; Dead 2; Idle 3 + ServerState = 173 // integer + + // When a home server is marked "dead" or "alive" + ServerTimeOfDeath = 174 // date + ServerTimeOfLife = 175 // date + + StartTime = 176 // date + HUPTime = 177 // date + + EmaWindow = 178 // integer + EmaUsecWindow1 = 179 // integer + EmaUsecWindow10 = 180 // integer + + QueuePPSIn = 181 // integer + QueuePPSOut = 182 // integer + QueueUsePercentage = 183 // integer + LastPacketRecv = 184 // date + LastPacketSent = 185 // date + StatsError = 187 // string ) -// GetValue returns attribute value. -func GetValue(p *radius.Packet, typ byte) (value uint32, err error) { +// GetInt returns attribute value. +func GetInt(p *radius.Packet, typ byte) (value uint32, err error) { a, ok := lookupVendor(p, typ) if !ok { err = radius.ErrNoAttribute @@ -87,6 +114,38 @@ func GetValue(p *radius.Packet, typ byte) (value uint32, err error) { return } +// GetString returns attribute value. +func GetString(p *radius.Packet, typ byte) (string, error) { + a, ok := lookupVendor(p, typ) + if !ok { + return "", radius.ErrNoAttribute + } + return radius.String(a), nil +} + +// GetDate returns attribute value. +func GetDate(p *radius.Packet, typ byte) (time.Time, error) { + a, ok := lookupVendor(p, typ) + if !ok { + return time.Time{}, radius.ErrNoAttribute + } + return radius.Date(a) +} + +// GetIP returns attribute value. +func GetIP(p *radius.Packet, typ byte) (string, error) { + a, ok := lookupVendor(p, typ) + if !ok { + return "", radius.ErrNoAttribute + } + ip, err := radius.IPAddr(a) + if err != nil { + return "", err + } + + return ip.String(), nil +} + // SetValue sets attribute value. func SetValue(p *radius.Packet, typ byte, attr radius.Attribute) (err error) { err = setVendor(p, typ, attr) diff --git a/main.go b/main.go index 1c29b01..fe234ac 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -22,6 +23,7 @@ func main() { metricsPath := flag.String("web.telemetry-path", "/metrics", "A path under which to expose metrics.") radiusTimeout := flag.Int("radius.timeout", 5000, "Timeout, in milliseconds.") radiusAddr := flag.String("radius.address", getEnv("RADIUS_ADDR", "127.0.0.1:18121"), "Address of FreeRADIUS status server.") + homeServers := flag.String("radius.homeservers", getEnv("RADIUS_HOMESERVERS", ""), "List of FreeRADIUS home servers to check, e.g. '172.28.1.2:1812,172.28.1.3:1812'.") radiusSecret := flag.String("radius.secret", getEnv("RADIUS_SECRET", "adminsecret"), "FreeRADIUS client secret.") appVersion := flag.Bool("version", false, "Display version information") @@ -34,7 +36,9 @@ func main() { registry := prometheus.NewRegistry() - radiusClient, err := client.NewFreeRADIUSClient(*radiusAddr, *radiusSecret, *radiusTimeout) + hs := strings.Split(*homeServers, ",") + + radiusClient, err := client.NewFreeRADIUSClient(*radiusAddr, hs, *radiusSecret, *radiusTimeout) if err != nil { log.Fatal(err) }