2021-03-25 14:46:25 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2021-05-14 19:22:42 +02:00
|
|
|
"flag"
|
2021-03-25 14:46:25 +01:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2021-03-26 13:08:44 +01:00
|
|
|
"strings"
|
2022-01-21 09:59:57 +01:00
|
|
|
|
|
|
|
"github.com/ClusterCockpit/cc-metric-collector/collectors"
|
|
|
|
"github.com/ClusterCockpit/cc-metric-collector/receivers"
|
|
|
|
"github.com/ClusterCockpit/cc-metric-collector/sinks"
|
2021-03-25 14:46:25 +01:00
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
// "strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
2021-03-25 17:47:08 +01:00
|
|
|
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger"
|
2022-01-25 15:37:43 +01:00
|
|
|
lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric"
|
|
|
|
mr "github.com/ClusterCockpit/cc-metric-collector/internal/metricRouter"
|
|
|
|
mct "github.com/ClusterCockpit/cc-metric-collector/internal/multiChanTicker"
|
|
|
|
)
|
2021-05-18 15:16:10 +02:00
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
type CentralConfigFile struct {
|
|
|
|
Interval int `json:"interval"`
|
|
|
|
Duration int `json:"duration"`
|
|
|
|
Pidfile string `json:"pidfile,omitempty"`
|
|
|
|
CollectorConfigFile string `json:"collectors"`
|
|
|
|
RouterConfigFile string `json:"router"`
|
|
|
|
SinkConfigFile string `json:"sinks"`
|
|
|
|
ReceiverConfigFile string `json:"receivers,omitempty"`
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
func LoadCentralConfiguration(file string, config *CentralConfigFile) error {
|
2021-03-25 14:46:25 +01:00
|
|
|
configFile, err := os.Open(file)
|
|
|
|
defer configFile.Close()
|
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2021-05-11 12:41:29 +02:00
|
|
|
return err
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
|
|
|
jsonParser := json.NewDecoder(configFile)
|
2021-11-25 14:04:03 +01:00
|
|
|
err = jsonParser.Decode(config)
|
2021-03-25 14:46:25 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
type RuntimeConfig struct {
|
|
|
|
Hostname string
|
|
|
|
Interval time.Duration
|
|
|
|
Duration time.Duration
|
|
|
|
CliArgs map[string]string
|
|
|
|
ConfigFile CentralConfigFile
|
|
|
|
|
|
|
|
Router mr.MetricRouter
|
|
|
|
CollectManager collectors.CollectorManager
|
|
|
|
SinkManager sinks.SinkManager
|
|
|
|
ReceiveManager receivers.ReceiveManager
|
|
|
|
Ticker mct.MultiChanTicker
|
|
|
|
|
|
|
|
Channels []chan lp.CCMetric
|
|
|
|
Sync sync.WaitGroup
|
|
|
|
}
|
|
|
|
|
|
|
|
func prepare_runcfg() RuntimeConfig {
|
|
|
|
return RuntimeConfig{
|
|
|
|
Router: nil,
|
|
|
|
CollectManager: nil,
|
|
|
|
SinkManager: nil,
|
|
|
|
ReceiveManager: nil,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Structure of the configuration file
|
|
|
|
//type GlobalConfig struct {
|
|
|
|
// Sink sinks.SinkConfig `json:"sink"`
|
|
|
|
// Interval int `json:"interval"`
|
|
|
|
// Duration int `json:"duration"`
|
|
|
|
// Collectors []string `json:"collectors"`
|
|
|
|
// Receiver receivers.ReceiverConfig `json:"receiver"`
|
|
|
|
// DefTags map[string]string `json:"default_tags"`
|
|
|
|
// CollectConfigs map[string]json.RawMessage `json:"collect_config"`
|
|
|
|
//}
|
|
|
|
|
|
|
|
//// Load JSON configuration file
|
|
|
|
//func LoadConfiguration(file string, config *GlobalConfig) error {
|
|
|
|
// configFile, err := os.Open(file)
|
|
|
|
// defer configFile.Close()
|
|
|
|
// if err != nil {
|
|
|
|
// fmt.Println(err.Error())
|
|
|
|
// return err
|
|
|
|
// }
|
|
|
|
// jsonParser := json.NewDecoder(configFile)
|
|
|
|
// err = jsonParser.Decode(config)
|
|
|
|
// return err
|
|
|
|
//}
|
|
|
|
|
2021-05-11 13:16:35 +02:00
|
|
|
func ReadCli() map[string]string {
|
2021-05-14 19:22:42 +02:00
|
|
|
var m map[string]string
|
|
|
|
cfg := flag.String("config", "./config.json", "Path to configuration file")
|
|
|
|
logfile := flag.String("log", "stderr", "Path for logfile")
|
2021-05-29 03:40:12 +02:00
|
|
|
pidfile := flag.String("pidfile", "/var/run/cc-metric-collector.pid", "Path for PID file")
|
2021-11-25 14:04:03 +01:00
|
|
|
once := flag.Bool("once", false, "Run all collectors only once")
|
2022-01-25 17:43:10 +01:00
|
|
|
debug := flag.Bool("debug", false, "Activate debug output")
|
2021-05-14 19:22:42 +02:00
|
|
|
flag.Parse()
|
|
|
|
m = make(map[string]string)
|
|
|
|
m["configfile"] = *cfg
|
|
|
|
m["logfile"] = *logfile
|
2021-05-29 03:40:12 +02:00
|
|
|
m["pidfile"] = *pidfile
|
2021-11-25 14:04:03 +01:00
|
|
|
if *once {
|
2021-11-25 15:11:39 +01:00
|
|
|
m["once"] = "true"
|
2021-11-25 14:04:03 +01:00
|
|
|
} else {
|
2021-11-25 15:11:39 +01:00
|
|
|
m["once"] = "false"
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2022-01-25 17:43:10 +01:00
|
|
|
if *debug {
|
|
|
|
m["debug"] = "true"
|
|
|
|
cclog.SetDebug()
|
|
|
|
} else {
|
|
|
|
m["debug"] = "false"
|
|
|
|
}
|
2021-05-14 19:22:42 +02:00
|
|
|
return m
|
2021-05-11 13:16:35 +02:00
|
|
|
}
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
//func SetLogging(logfile string) error {
|
|
|
|
// var file *os.File
|
|
|
|
// var err error
|
|
|
|
// if logfile != "stderr" {
|
|
|
|
// file, err = os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
|
|
|
// if err != nil {
|
|
|
|
// log.Fatal(err)
|
|
|
|
// return err
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// file = os.Stderr
|
|
|
|
// }
|
|
|
|
// log.SetOutput(file)
|
|
|
|
// return nil
|
|
|
|
//}
|
2021-05-11 12:41:29 +02:00
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
//func CreatePidfile(pidfile string) error {
|
|
|
|
// file, err := os.OpenFile(pidfile, os.O_CREATE|os.O_RDWR, 0600)
|
|
|
|
// if err != nil {
|
|
|
|
// log.Print(err)
|
|
|
|
// return err
|
|
|
|
// }
|
|
|
|
// file.Write([]byte(fmt.Sprintf("%d", os.Getpid())))
|
|
|
|
// file.Close()
|
|
|
|
// return nil
|
|
|
|
//}
|
2021-05-29 03:40:12 +02:00
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
//func RemovePidfile(pidfile string) error {
|
|
|
|
// info, err := os.Stat(pidfile)
|
|
|
|
// if !os.IsNotExist(err) && !info.IsDir() {
|
|
|
|
// os.Remove(pidfile)
|
|
|
|
// }
|
|
|
|
// return nil
|
|
|
|
//}
|
2021-05-29 03:40:12 +02:00
|
|
|
|
2021-11-25 14:04:03 +01:00
|
|
|
// General shutdown function that gets executed in case of interrupt or graceful shutdown
|
2022-01-25 15:37:43 +01:00
|
|
|
func shutdown(config *RuntimeConfig) {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Info("Shutdown...")
|
2022-01-26 17:08:53 +01:00
|
|
|
config.Ticker.Close()
|
2022-01-25 15:37:43 +01:00
|
|
|
if config.CollectManager != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Debug("Shutdown CollectManager...")
|
2022-01-25 15:37:43 +01:00
|
|
|
config.CollectManager.Close()
|
|
|
|
}
|
|
|
|
if config.ReceiveManager != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Debug("Shutdown ReceiveManager...")
|
2022-01-25 15:37:43 +01:00
|
|
|
config.ReceiveManager.Close()
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if config.Router != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Debug("Shutdown Router...")
|
2022-01-25 15:37:43 +01:00
|
|
|
config.Router.Close()
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if config.SinkManager != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Debug("Shutdown SinkManager...")
|
2022-01-25 15:37:43 +01:00
|
|
|
config.SinkManager.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// pidfile := config.ConfigFile.Pidfile
|
|
|
|
// RemovePidfile(pidfile)
|
|
|
|
// pidfile = config.CliArgs["pidfile"]
|
|
|
|
// RemovePidfile(pidfile)
|
|
|
|
config.Sync.Done()
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
|
|
|
|
2021-03-26 13:08:44 +01:00
|
|
|
// Register an interrupt handler for Ctrl+C and similar. At signal,
|
|
|
|
// all collectors are closed
|
2022-01-25 15:37:43 +01:00
|
|
|
func prepare_shutdown(config *RuntimeConfig) {
|
2021-03-25 14:46:25 +01:00
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, os.Interrupt)
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
go func(config *RuntimeConfig) {
|
2021-03-25 14:46:25 +01:00
|
|
|
<-sigs
|
2022-01-25 15:37:43 +01:00
|
|
|
shutdown(config)
|
|
|
|
}(config)
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
|
|
|
|
2022-01-25 15:46:41 +01:00
|
|
|
func mainFunc() int {
|
2022-01-25 15:37:43 +01:00
|
|
|
var err error
|
|
|
|
use_recv := false
|
|
|
|
|
|
|
|
rcfg := prepare_runcfg()
|
|
|
|
rcfg.CliArgs = ReadCli()
|
2021-03-25 14:46:25 +01:00
|
|
|
|
2021-03-26 13:08:44 +01:00
|
|
|
// Load and check configuration
|
2022-01-25 15:37:43 +01:00
|
|
|
err = LoadCentralConfiguration(rcfg.CliArgs["configfile"], &rcfg.ConfigFile)
|
2021-05-14 19:22:42 +02:00
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error("Error reading configuration file ", rcfg.CliArgs["configfile"], ": ", err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-05-11 12:41:29 +02:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if rcfg.ConfigFile.Interval <= 0 || time.Duration(rcfg.ConfigFile.Interval)*time.Second <= 0 {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error("Configuration value 'interval' must be greater than zero")
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
rcfg.Interval = time.Duration(rcfg.ConfigFile.Interval) * time.Second
|
|
|
|
if rcfg.ConfigFile.Duration <= 0 || time.Duration(rcfg.ConfigFile.Duration)*time.Second <= 0 {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error("Configuration value 'duration' must be greater than zero")
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
rcfg.Duration = time.Duration(rcfg.ConfigFile.Duration) * time.Second
|
|
|
|
|
|
|
|
rcfg.Hostname, err = os.Hostname()
|
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-03-26 13:08:44 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
// Drop domain part of host name
|
|
|
|
rcfg.Hostname = strings.SplitN(rcfg.Hostname, `.`, 2)[0]
|
|
|
|
// err = CreatePidfile(rcfg.CliArgs["pidfile"])
|
2022-01-25 17:43:10 +01:00
|
|
|
|
|
|
|
if rcfg.CliArgs["logfile"] != "stderr" {
|
2022-01-26 17:08:53 +01:00
|
|
|
cclog.SetOutput(rcfg.CliArgs["logfile"])
|
2022-01-25 17:43:10 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
// err = SetLogging(rcfg.CliArgs["logfile"])
|
|
|
|
// if err != nil {
|
|
|
|
// log.Print("Error setting up logging system to ", rcfg.CliArgs["logfile"], " on ", rcfg.Hostname)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
rcfg.Ticker = mct.NewTicker(rcfg.Interval)
|
|
|
|
if len(rcfg.ConfigFile.RouterConfigFile) > 0 {
|
|
|
|
rcfg.Router, err = mr.New(rcfg.Ticker, &rcfg.Sync, rcfg.ConfigFile.RouterConfigFile)
|
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-03-26 13:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if len(rcfg.ConfigFile.SinkConfigFile) > 0 {
|
|
|
|
rcfg.SinkManager, err = sinks.New(&rcfg.Sync, rcfg.ConfigFile.SinkConfigFile)
|
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-05-18 15:16:10 +02:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
RouterToSinksChannel := make(chan lp.CCMetric)
|
|
|
|
rcfg.SinkManager.AddInput(RouterToSinksChannel)
|
|
|
|
rcfg.Router.AddOutput(RouterToSinksChannel)
|
2021-05-18 15:16:10 +02:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if len(rcfg.ConfigFile.CollectorConfigFile) > 0 {
|
|
|
|
rcfg.CollectManager, err = collectors.New(rcfg.Ticker, rcfg.Duration, &rcfg.Sync, rcfg.ConfigFile.CollectorConfigFile)
|
2021-05-14 19:22:42 +02:00
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2021-05-14 19:22:42 +02:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
CollectToRouterChannel := make(chan lp.CCMetric)
|
|
|
|
rcfg.CollectManager.AddOutput(CollectToRouterChannel)
|
2022-01-26 17:08:53 +01:00
|
|
|
rcfg.Router.AddCollectorInput(CollectToRouterChannel)
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
if len(rcfg.ConfigFile.ReceiverConfigFile) > 0 {
|
|
|
|
rcfg.ReceiveManager, err = receivers.New(&rcfg.Sync, rcfg.ConfigFile.ReceiverConfigFile)
|
|
|
|
if err != nil {
|
2022-01-25 16:40:02 +01:00
|
|
|
cclog.Error(err.Error())
|
2022-01-25 15:46:41 +01:00
|
|
|
return 1
|
2022-01-25 15:37:43 +01:00
|
|
|
}
|
|
|
|
ReceiveToRouterChannel := make(chan lp.CCMetric)
|
|
|
|
rcfg.ReceiveManager.AddOutput(ReceiveToRouterChannel)
|
2022-01-26 17:08:53 +01:00
|
|
|
rcfg.Router.AddReceiverInput(ReceiveToRouterChannel)
|
2022-01-25 15:37:43 +01:00
|
|
|
use_recv = true
|
2021-11-25 15:11:39 +01:00
|
|
|
}
|
2022-01-25 15:37:43 +01:00
|
|
|
prepare_shutdown(&rcfg)
|
|
|
|
rcfg.Sync.Add(1)
|
|
|
|
rcfg.Router.Start()
|
|
|
|
rcfg.SinkManager.Start()
|
|
|
|
rcfg.CollectManager.Start()
|
2021-03-26 10:19:54 +01:00
|
|
|
|
2021-05-18 15:16:10 +02:00
|
|
|
if use_recv {
|
2022-01-25 15:37:43 +01:00
|
|
|
rcfg.ReceiveManager.Start()
|
2021-05-18 15:16:10 +02:00
|
|
|
}
|
|
|
|
|
2022-01-25 15:46:41 +01:00
|
|
|
// Wait until one tick has passed. This is a workaround
|
|
|
|
if rcfg.CliArgs["once"] == "true" {
|
2022-01-25 17:49:15 +01:00
|
|
|
x := 1.2 * float64(rcfg.ConfigFile.Interval)
|
2022-01-25 15:46:41 +01:00
|
|
|
time.Sleep(time.Duration(int(x)) * time.Second)
|
|
|
|
shutdown(&rcfg)
|
|
|
|
}
|
|
|
|
|
2021-03-26 13:08:44 +01:00
|
|
|
// Wait until receiving an interrupt
|
2022-01-25 15:37:43 +01:00
|
|
|
rcfg.Sync.Wait()
|
2022-01-25 15:46:41 +01:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
exitCode := mainFunc()
|
|
|
|
os.Exit(exitCode)
|
2021-03-25 14:46:25 +01:00
|
|
|
}
|