From 45948f76dd0a131c3a3b0453ae00c2c3104fd3d5 Mon Sep 17 00:00:00 2001 From: Emiliano Ciavatta Date: Thu, 8 Oct 2020 16:58:40 +0200 Subject: Add resource controller --- VERSION | 2 +- application_router.go | 10 +++++- caronte.go | 6 +++- go.mod | 3 ++ go.sum | 12 +++++++ resources_controller.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 8 +++++ 7 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 resources_controller.go diff --git a/VERSION b/VERSION index ce609ca..bc1f22f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8 \ No newline at end of file +v0.20.10 \ No newline at end of file diff --git a/application_router.go b/application_router.go index 334e9f3..da71538 100644 --- a/application_router.go +++ b/application_router.go @@ -14,7 +14,7 @@ import ( ) func CreateApplicationRouter(applicationContext *ApplicationContext, - notificationController *NotificationController) *gin.Engine { + notificationController *NotificationController, resourcesController *ResourcesController) *gin.Engine { router := gin.New() router.Use(gin.Logger()) router.Use(gin.Recovery()) @@ -342,6 +342,14 @@ func CreateApplicationRouter(applicationContext *ApplicationContext, success(c, applicationContext.StatisticsController.GetStatistics(c, filter)) }) + + api.GET("/resources/system", func(c *gin.Context) { + success(c, resourcesController.GetSystemStats(c)) + }) + + api.GET("/resources/process", func(c *gin.Context) { + success(c, resourcesController.GetProcessStats(c)) + }) } return router diff --git a/caronte.go b/caronte.go index 288563c..d999724 100644 --- a/caronte.go +++ b/caronte.go @@ -35,7 +35,11 @@ func main() { notificationController := NewNotificationController(applicationContext) go notificationController.Run() - applicationRouter := CreateApplicationRouter(applicationContext, notificationController) + + resourcesController := NewResourcesController(notificationController) + go resourcesController.Run() + + applicationRouter := CreateApplicationRouter(applicationContext, notificationController, resourcesController) if applicationRouter.Run(fmt.Sprintf("%s:%v", *bindAddress, *bindPort)) != nil { log.WithError(err).WithFields(logFields).Fatal("failed to create the server") } diff --git a/go.mod b/go.mod index 404f64c..7834dbe 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,18 @@ module github.com/eciavatta/caronte go 1.14 require ( + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/flier/gohs v1.0.0 github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 github.com/gin-gonic/gin v1.6.2 + github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-playground/validator/v10 v10.2.0 github.com/golang/protobuf v1.3.5 // indirect github.com/google/gopacket v1.1.17 github.com/gorilla/websocket v1.4.2 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/shirou/gopsutil v2.20.9+incompatible github.com/sirupsen/logrus v1.4.2 github.com/smartystreets/assertions v1.0.0 // indirect github.com/stretchr/testify v1.4.0 diff --git a/go.sum b/go.sum index d29e0cb..b507337 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -10,6 +12,8 @@ github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 h1:MrIm8EEPue08J github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg= github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -52,6 +56,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.14/go.mod h1:UCLx9mCmAwsVbn6qQl1WIEt2SO7Nd2fD0th1TBAsqBw= github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= @@ -104,9 +109,13 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/shirou/gopsutil v2.20.9+incompatible h1:msXs2frUV+O/JLva9EDLpuJ84PrFsdCTCQex8PUdtkQ= +github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= @@ -152,6 +161,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -161,6 +172,7 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200406155108-e3b113bbe6a4 h1:c1Sgqkh8v6ZxafNGG64r8C8UisIW2TKMJN8P86tKjr0= diff --git a/resources_controller.go b/resources_controller.go new file mode 100644 index 0000000..050157a --- /dev/null +++ b/resources_controller.go @@ -0,0 +1,91 @@ +package main + +import ( + "context" + "github.com/gin-gonic/gin" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/mem" + log "github.com/sirupsen/logrus" + "sync" + "time" +) + +const ( + averageCPUPercentAlertThreshold = 90.0 + averageCPUPercentAlertMinInterval = 120.0 +) + +type SystemStats struct { + VirtualMemory *mem.VirtualMemoryStat `json:"virtual_memory"` + CPUTimes []cpu.TimesStat `json:"cpu_times"` + CPUPercents []float64 `json:"cpu_percents"` + DiskUsage *disk.UsageStat `json:"disk_usage"` +} + +type ResourcesController struct { + notificationController *NotificationController + lastCPUPercent []float64 + mutex sync.Mutex +} + +func NewResourcesController(notificationController *NotificationController) *ResourcesController { + return &ResourcesController{ + notificationController: notificationController, + } +} + +func (csc *ResourcesController) GetProcessStats(c context.Context) interface{} { + return nil +} + +func (csc *ResourcesController) GetSystemStats(c context.Context) SystemStats { + virtualMemory, err := mem.VirtualMemoryWithContext(c) + if err != nil { + log.WithError(err).Panic("failed to retrieve virtual memory") + } + cpuTimes, err := cpu.TimesWithContext(c, true) + if err != nil { + log.WithError(err).Panic("failed to retrieve cpu times") + } + diskUsage, err := disk.UsageWithContext(c, "/") + if err != nil { + log.WithError(err).Panic("failed to retrieve disk usage") + } + + defer csc.mutex.Unlock() + csc.mutex.Lock() + + return SystemStats{ + VirtualMemory: virtualMemory, + CPUTimes: cpuTimes, + DiskUsage: diskUsage, + CPUPercents: csc.lastCPUPercent, + } +} + +func (csc *ResourcesController) Run() { + interval, _ := time.ParseDuration("3s") + var lastAlertTime time.Time + + for { + cpuPercent, err := cpu.Percent(interval, true) + if err != nil { + log.WithError(err).Error("failed to retrieve cpu percent") + return + } + + csc.mutex.Lock() + csc.lastCPUPercent = cpuPercent + csc.mutex.Unlock() + + avg := Average(cpuPercent) + if avg > averageCPUPercentAlertThreshold && time.Now().Sub(lastAlertTime).Seconds() > averageCPUPercentAlertMinInterval { + csc.notificationController.Notify("resources.cpu_alert", "alert", gin.H{ + "cpu_percent": cpuPercent, + }) + log.WithField("cpu_percent", cpuPercent).Warn("cpu percent usage has exceeded the limit threshold") + lastAlertTime = time.Now() + } + } +} diff --git a/utils.go b/utils.go index a14fdca..ec5a807 100644 --- a/utils.go +++ b/utils.go @@ -151,3 +151,11 @@ func ParseIPNet(address string) *net.IPNet { return network } + +func Average(array []float64) float64 { + var sum float64 + for _, f := range array { + sum += f + } + return sum / float64(len(array)) +} -- cgit v1.2.3-70-g09d2