diff --git a/gluster-exporter/metric_daemon_status.go b/gluster-exporter/metric_daemon_status.go new file mode 100644 index 0000000..d25ad1b --- /dev/null +++ b/gluster-exporter/metric_daemon_status.go @@ -0,0 +1,125 @@ +package main + +import ( + "errors" + "io/ioutil" + "net/http" + "strings" + + "github.com/gluster/gluster-prometheus/pkg/glusterutils" + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" +) + +const ( + // GDaemonLabel provides static label to info/error provided from this metrics + GDaemonLabel = "Gluster_Daemon_Status" +) + +func gdStatus(conf *glusterutils.Config) error { + if conf.GlusterMgmt == glusterutils.MgmtGlusterd { + if _, err := glusterutils.ExecuteCmd("ps --no-header -ww -o pid,comm -C glusterd"); err != nil { + return err + } + } else if conf.GlusterMgmt == glusterutils.MgmtGlusterd2 { + if conf.Glusterd2Endpoint == "" { + return errors.New("[" + GDaemonLabel + "] Empty GD2 Endpoint") + } + pingURL := strings.Join([]string{conf.Glusterd2Endpoint, "ping"}, "/") + // #nosec + resp, err := http.Get(pingURL) + if err != nil { + return errors.New("[" + GDaemonLabel + "]" + "Endpoint Get Error: " + err.Error()) + } + _, err = ioutil.ReadAll(resp.Body) + if err != nil { + return errors.New("[" + GDaemonLabel + "]" + "Endpoint Read Error: " + err.Error()) + } + } + return nil +} + +var ( + // metric labels + gdStatusLbl = []MetricLabel{ + { + Name: "name", + Help: "Metric name, for which the status is collected", + }, + { + Name: "gdType", + Help: "Type of gluster daemon / service running (GD1 or GD2)", + }, + { + Name: "peerID", + Help: "Peer ID of the host for which this metric status is updated", + }, + } + gExprtrLbl = []MetricLabel{ + { + Name: "name", + Help: "Metric Name to be collected", + }, + { + Name: "peerID", + Help: "Peer ID of the host, on which the data is collected", + }, + } + glusterDaemonStatus = newPrometheusGaugeVec(Metric{ + Namespace: "gluster", + Name: "daemon_status", + Help: "Status of gluster management daemon (1 = running and 0 = not-running)", + LongHelp: "", + Labels: gdStatusLbl, + }) + glusterExporterStatus = newPrometheusGaugeVec(Metric{ + Namespace: "gluster", + Name: "exporter_status", + Help: "Status of gluster exporter (will be set 1 always)", + LongHelp: "", + Labels: gExprtrLbl, + }) +) + +// gDaemonStatus registering function, +// provides the status of services running on the machine +func gDaemonStatus() error { + var conf *glusterutils.Config + if gluster == nil { + return errors.New("[" + GDaemonLabel + "] Unable to get a 'GInterface' object") + } + peerID, err := gluster.LocalPeerID() + if err != nil { + return errors.New("[" + GDaemonLabel + "] Getting Peer ID failed: " + err.Error()) + } + conf, err = glusterutils.GDConfigFromInterface(gluster) + if err != nil { + return errors.New("[" + GDaemonLabel + "] Error: " + err.Error()) + } + log.Println("GD Management: ", conf.GlusterMgmt) + genrlLbls := prometheus.Labels{ + "name": "Glusterd_Status", + "gdType": conf.GlusterMgmt, + "peerID": peerID, + } + err = gdStatus(conf) + if err != nil { + log.WithError(err).WithFields(log.Fields{ + "peer": peerID, + "name": "Glusterd_Status", + }).Errorln("["+GDaemonLabel+"] Error:", err) + glusterDaemonStatus.With(genrlLbls).Set(float64(0)) + } else { + glusterDaemonStatus.With(genrlLbls).Set(float64(1)) + } + genrlLbls = prometheus.Labels{ + "name": "Gluster_Exporter_Status", + "peerID": peerID, + } + glusterExporterStatus.With(genrlLbls).Set(float64(1)) + return nil +} + +func init() { + registerMetric("gluster_daemon_status", gDaemonStatus) +} diff --git a/pkg/glusterutils/exporterd.go b/pkg/glusterutils/exporterd.go index daefb61..6a84601 100644 --- a/pkg/glusterutils/exporterd.go +++ b/pkg/glusterutils/exporterd.go @@ -15,6 +15,22 @@ var ( peerIDPattern = regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}") ) +// GDConfigFromInterface checks the given interface is compatible with 'GDConfigInterface' +// and returns a pointer to glusterutils.Config +func GDConfigFromInterface(iFace interface{}) (*Config, error) { + if gdConf, ok := iFace.(GDConfigInterface); ok { + return gdConf.Config(), nil + } + return nil, errors.New("Incompatible interface type, " + + "cannot be converted to 'GDConfigInterface'") +} + +// Config returns the configuration associated with it +// and makes it compatible with 'GDConfigInterface' +func (g *GD1) Config() *Config { + return g.config +} + // IsLeader returns true or false based on whether the node is the leader of the cluster or not func (g *GD1) IsLeader() (bool, error) { setDefaultConfig(g.config) @@ -42,6 +58,12 @@ func (g *GD1) IsLeader() (bool, error) { return false, nil } +// Config returns the configuration associated with it +// and makes it compatible with 'GDConfigInterface' +func (g *GD2) Config() *Config { + return g.config +} + // IsLeader returns true or false based on whether the node is the leader of the cluster or not func (g *GD2) IsLeader() (bool, error) { peerList, err := g.Peers() diff --git a/pkg/glusterutils/types.go b/pkg/glusterutils/types.go index 456c2d4..37dbecc 100644 --- a/pkg/glusterutils/types.go +++ b/pkg/glusterutils/types.go @@ -144,6 +144,11 @@ type GInterface interface { EnableVolumeProfiling(volinfo Volume) error } +// GDConfigInterface returns the configuration of the GD +type GDConfigInterface interface { + Config() *Config +} + // FopStat defines file ops related details type FopStat struct { Name string