func (i *Inspeqtor) Parse() error { i.ServiceManagers = services.Detect() config, err := ParseGlobal(i.RootDir) if err != nil { return err } util.DebugDebug("Global config: %+v", config) i.GlobalConfig = config host, err := ParseHost(i.GlobalConfig, i.RootDir+"/host.inq") if err != nil { return err } i.Host = host services, err := ParseServices(i.GlobalConfig, i.RootDir+"/services.d") if err != nil { return err } i.Services = services util.DebugDebug("Config: %+v", config) util.DebugDebug("Host: %+v", host) for _, val := range services { util.DebugDebug("Service: %+v", val) } return nil }
/* Collecting total RSS for a process is actually rather involved on Linux. Because of this, we only collect the value if the user defines a rule for it. This is a "dynamicCollector". */ func totalRssCollector(mypid int, ps *processStorage) error { matches, err := filepath.Glob(fmt.Sprintf("%s/[1-9][0-9]*/status", ps.path)) if err != nil { return err } var live []processEntry for _, file := range matches { pe := processEntry{} data, err := ioutil.ReadFile(file) if err != nil { // race condition between globbing and reading, process // can disappear at any moment continue } lines, err := util.ReadLines(data) if err != nil { return err } for _, line := range lines { if line[0] == 'V' || line[0] == 'P' { items := strings.Split(line, ":") switch items[0] { case "Pid": pid, err := strconv.Atoi(strings.TrimSpace(items[1])) if err != nil { return err } pe.pid = pid case "PPid": ppid, err := strconv.Atoi(strings.TrimSpace(items[1])) if err != nil { return err } pe.ppid = ppid case "VmRSS": vals := strings.Fields(items[1]) val, err := strconv.ParseInt(vals[0], 10, 64) if err != nil { return err } pe.rss = val * 1024 } } } if pe.rss != 0 { live = append(live, pe) } } util.DebugDebug("Calculating %d processes", len(live)) rss := memoryFor(live, mypid) util.DebugDebug("Total RSS for %d: %d", mypid, rss) ps.Save("memory", "total_rss", float64(rss)) return nil }
func easyMemstatsClient(url string, msp func(*http.Response) (int64, error)) (int64, error) { util.DebugDebug("Fetching memstats from %s", url) resp, err := http.Get(url) if err != nil { return 0, err } defer resp.Body.Close() return msp(resp) }
func (svc *Service) Reload() error { go func() { util.Debug("Reloading %s", svc.Name()) err := svc.Manager.Reload(svc.Name()) if err != nil { util.Warn(err.Error()) } else { util.DebugDebug("Reloaded %s", svc.Name()) } }() return nil }
/* Parses the service-specific rules in /etc/inspeqtor/services.d/*.inq */ func ParseServices(global *ConfigFile, confDir string) ([]Checkable, error) { util.Debug("Parsing config in " + confDir) files, err := filepath.Glob(confDir + "/*.inq") if err != nil { return nil, err } var checks []Checkable for _, filename := range files { util.DebugDebug("Parsing " + filename) data, err := ioutil.ReadFile(filename) if err != nil { return nil, err } s := lexer.NewLexer([]byte(data)) p := parser.NewParser() obj, err := p.Parse(s) if err != nil { util.Warn("Unable to parse " + filename + ": " + err.Error()) continue } switch x := obj.(type) { case *ast.ProcessCheck: svc, err := BuildService(global, x) if err != nil { return nil, err } util.DebugDebug("Service: %+v", *svc) checks = append(checks, svc) default: return nil, fmt.Errorf("Invalid configuration file: %s", filename) } } return checks, nil }
func parseJobs(global *inspeqtor.ConfigFile, confDir string) (map[string]*Job, error) { util.Debug("Parsing jobs in " + confDir) files, err := filepath.Glob(confDir + "/jobs.d/*.inq") if err != nil { return nil, err } jobs := map[string]*Job{} for _, filename := range files { util.DebugDebug("Parsing " + filename) data, err := ioutil.ReadFile(filename) if err != nil { return nil, err } s := lexer.NewLexer([]byte(data)) p := parser.NewParser() obj, err := p.Parse(s) if err != nil { util.Warn("Unable to parse " + filename + ": " + err.Error()) continue } astcontent := obj.(*ast.Content) for _, astjob := range astcontent.Jobs { if _, ok := jobs[astjob.Name]; ok { return nil, fmt.Errorf("Duplicate job %s", astjob.Name) } j := New(astjob.Name, astjob.Interval, astcontent.Parameters) owner := j.Parameters["owner"] route := global.AlertRoutes[owner] if owner == "" && route == nil { return nil, fmt.Errorf("No default alert route configured!") } if route == nil { return nil, fmt.Errorf("No such alert route: %s", owner) } alert, err := inspeqtor.Actions["alert"](j, route) if err != nil { return nil, err } j.alerter = alert jobs[astjob.Name] = j } } return jobs, nil }
func (svc *Service) Restart() error { svc.Process.Pid = 0 svc.Process.Status = services.Starting go func() { util.Debug("Restarting %s", svc.Name()) err := svc.Manager.Restart(svc.Name()) if err != nil { util.Warn(err.Error()) } else { util.DebugDebug("Restarted %s", svc.Name()) } }() return nil }
/* Parses the host-specific rules in /etc/inspeqtor/host.inq */ func ParseHost(global *ConfigFile, hostInq string) (*Host, error) { var host *Host result, err := util.FileExists(hostInq) if err != nil { return nil, err } if !result { return nil, fmt.Errorf("Missing required file: %s", hostInq) } util.DebugDebug("Parsing " + hostInq) data, err := ioutil.ReadFile(hostInq) if err != nil { return nil, err } s := lexer.NewLexer([]byte(data)) p := parser.NewParser() obj, err := p.Parse(s) if err != nil { return nil, err } switch x := obj.(type) { case *ast.HostCheck: host, err = BuildHost(global, x) if err != nil { return nil, err } util.DebugDebug("Host: %+v", *host) default: return nil, fmt.Errorf("Invalid host.inq configuration file") } return host, nil }
func convertService(global *ConfigFile, inqsvc *ast.ProcessCheck) (*Service, error) { rules := make([]*Rule, len(inqsvc.Rules)) storage := metrics.NewProcessStore("/proc", global.CycleTime) svc := &Service{&Entity{inqsvc.Name, nil, storage, inqsvc.Parameters}, nil, services.NewStatus(), nil} action, err := BuildAction(global, svc, &ast.SimpleAction{ActionName: "alert"}) if err != nil { return nil, err } svc.EventHandler = action for idx, rule := range inqsvc.Rules { rule, err := convertRule(global, svc, rule) if err != nil { return nil, err } util.DebugDebug("Rule: %+v", *rule) rules[idx] = rule } svc.rules = rules for _, r := range rules { _, err := storage.AddSource(r.MetricFamily, svc.Parameters()) if err != nil { return nil, err } err = storage.Watch(r.MetricFamily, r.MetricName) if err != nil { return nil, err } util.Debug("Watching %s:%s", r.MetricFamily, r.MetricName) } if len(inqsvc.Exposed) > 0 { err := BuildExpose(global, svc, inqsvc.Exposed, inqsvc.Parameters) if err != nil { return nil, err } } err = storage.Prepare() if err != nil { return nil, err } return svc, nil }
// this method never returns. // // since we can't test this method in an automated fashion, it should // contain as little logic as possible. func (i *Inspeqtor) runLoop() { util.DebugDebug("Resolving services") for _, svc := range i.Services { err := svc.Resolve(i.ServiceManagers) if err != nil { util.Warn(err.Error()) } } i.scanSystem() for { select { case <-time.After(time.Duration(i.GlobalConfig.CycleTime) * time.Second): i.scanSystem() case <-i.Stopping: util.Debug("Shutting down main run loop") return } } }
// GACK, so ugly func convertHost(global *ConfigFile, inqhost *ast.HostCheck) (*Host, error) { hostname, err := os.Hostname() if err != nil { return nil, err } storage := metrics.NewHostStore("/proc", global.CycleTime) h := &Host{&Entity{hostname, nil, storage, inqhost.Parameters}} rules := make([]*Rule, len(inqhost.Rules)) for idx, rule := range inqhost.Rules { rule, err := BuildRule(global, h, rule) util.DebugDebug("Rule: %+v", rule) if err != nil { return nil, err } err = storage.Watch(rule.MetricFamily, rule.MetricName) if err != nil { return nil, err } rules[idx] = rule } h.rules = rules return h, nil }