mirror of
https://github.com/digitalocean/ceph_exporter
synced 2025-02-21 13:56:48 +00:00
introduce version parser and IsAtLeast constraint
This commit is contained in:
parent
4e84633fc0
commit
13e97cd25d
98
exporter.go
98
exporter.go
@ -21,7 +21,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -32,6 +31,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/digitalocean/ceph_exporter/collectors"
|
||||
"github.com/digitalocean/ceph_exporter/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -42,6 +42,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// CephVersion is the parsed *version.Version from the Ceph version
|
||||
CephVersion *version.Version
|
||||
versionLock sync.Mutex
|
||||
|
||||
errCephVersionUnsupported = errors.New("ceph version unsupported")
|
||||
)
|
||||
|
||||
@ -77,10 +81,12 @@ func (ln emfileAwareTcpListener) Accept() (c net.Conn, err error) {
|
||||
// prometheus. It also implements a prometheus.Collector interface in order
|
||||
// to register it correctly.
|
||||
type CephExporter struct {
|
||||
mu sync.Mutex
|
||||
conn collectors.Conn
|
||||
collectors map[string][]prometheus.Collector
|
||||
logger *logrus.Logger
|
||||
mu sync.Mutex
|
||||
conn collectors.Conn
|
||||
cluster string
|
||||
config string
|
||||
rgwMode int
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// Verify that the exporter implements the interface correctly.
|
||||
@ -90,44 +96,37 @@ var _ prometheus.Collector = &CephExporter{}
|
||||
// to it. We can choose to enable a collector to extract stats out of by adding
|
||||
// it to the list of collectors.
|
||||
func NewCephExporter(conn collectors.Conn, cluster string, config string, rgwMode int, logger *logrus.Logger) *CephExporter {
|
||||
return &CephExporter{
|
||||
conn: conn,
|
||||
cluster: cluster,
|
||||
config: config,
|
||||
rgwMode: rgwMode,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CephExporter) getCollectors() []prometheus.Collector {
|
||||
standardCollectors := []prometheus.Collector{
|
||||
collectors.NewClusterUsageCollector(conn, cluster, logger),
|
||||
collectors.NewPoolUsageCollector(conn, cluster, logger),
|
||||
collectors.NewPoolInfoCollector(conn, cluster, logger),
|
||||
collectors.NewClusterHealthCollector(conn, cluster, logger),
|
||||
collectors.NewMonitorCollector(conn, cluster, logger),
|
||||
collectors.NewOSDCollector(conn, cluster, logger),
|
||||
collectors.NewClusterUsageCollector(c.conn, c.cluster, c.logger),
|
||||
collectors.NewPoolUsageCollector(c.conn, c.cluster, c.logger),
|
||||
collectors.NewPoolInfoCollector(c.conn, c.cluster, c.logger),
|
||||
collectors.NewClusterHealthCollector(c.conn, c.cluster, c.logger),
|
||||
collectors.NewMonitorCollector(c.conn, c.cluster, c.logger),
|
||||
collectors.NewOSDCollector(c.conn, c.cluster, c.logger),
|
||||
}
|
||||
|
||||
c := &CephExporter{
|
||||
conn: conn,
|
||||
collectors: map[string][]prometheus.Collector{
|
||||
"nautilus": standardCollectors,
|
||||
"octopus": standardCollectors,
|
||||
"pacific": standardCollectors,
|
||||
},
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
switch rgwMode {
|
||||
switch c.rgwMode {
|
||||
case collectors.RGWModeForeground:
|
||||
for version := range c.collectors {
|
||||
c.collectors[version] = append(c.collectors[version], collectors.NewRGWCollector(cluster, config, false, logger))
|
||||
}
|
||||
|
||||
standardCollectors = append(standardCollectors, collectors.NewRGWCollector(c.cluster, c.config, false, c.logger))
|
||||
case collectors.RGWModeBackground:
|
||||
for version := range c.collectors {
|
||||
c.collectors[version] = append(c.collectors[version], collectors.NewRGWCollector(cluster, config, true, logger))
|
||||
}
|
||||
|
||||
standardCollectors = append(standardCollectors, collectors.NewRGWCollector(c.cluster, c.config, true, c.logger))
|
||||
case collectors.RGWModeDisabled:
|
||||
// nothing to do
|
||||
|
||||
default:
|
||||
logger.WithField("rgwMode", rgwMode).Warn("RGW collector disabled due to invalid mode")
|
||||
c.logger.WithField("rgwMode", c.rgwMode).Warn("RGW collector disabled due to invalid mode")
|
||||
}
|
||||
|
||||
return c
|
||||
return standardCollectors
|
||||
}
|
||||
|
||||
func (c *CephExporter) cephVersionCmd() []byte {
|
||||
@ -142,10 +141,10 @@ func (c *CephExporter) cephVersionCmd() []byte {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *CephExporter) getCephVersion() (string, error) {
|
||||
func (c *CephExporter) setCephVersion() error {
|
||||
buf, _, err := c.conn.MonCommand(c.cephVersionCmd())
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
cephVersion := &struct {
|
||||
@ -154,30 +153,31 @@ func (c *CephExporter) getCephVersion() (string, error) {
|
||||
|
||||
err = json.Unmarshal(buf, cephVersion)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.Contains(cephVersion.Version, "nautilus") {
|
||||
return "nautilus", nil
|
||||
} else if strings.Contains(cephVersion.Version, "octopus") {
|
||||
return "octopus", nil
|
||||
} else if strings.Contains(cephVersion.Version, "pacific") {
|
||||
return "pacific", nil
|
||||
parsedVersion, err := version.ParseCephVersion(cephVersion.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return "", errCephVersionUnsupported
|
||||
versionLock.Lock()
|
||||
CephVersion = parsedVersion
|
||||
versionLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Describe sends all the descriptors of the collectors included to
|
||||
// the provided channel.
|
||||
func (c *CephExporter) Describe(ch chan<- *prometheus.Desc) {
|
||||
version, err := c.getCephVersion()
|
||||
err := c.setCephVersion()
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("failed to determine ceph version")
|
||||
c.logger.WithError(err).Error("failed to set ceph version")
|
||||
return
|
||||
}
|
||||
|
||||
for _, cc := range c.collectors[version] {
|
||||
for _, cc := range c.getCollectors() {
|
||||
cc.Describe(ch)
|
||||
}
|
||||
}
|
||||
@ -186,16 +186,16 @@ func (c *CephExporter) Describe(ch chan<- *prometheus.Desc) {
|
||||
// prometheus. Collect could be called several times concurrently
|
||||
// and thus its run is protected by a single mutex.
|
||||
func (c *CephExporter) Collect(ch chan<- prometheus.Metric) {
|
||||
version, err := c.getCephVersion()
|
||||
err := c.setCephVersion()
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("failed to determine ceph version")
|
||||
c.logger.WithError(err).Error("failed to set ceph version")
|
||||
return
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
for _, cc := range c.collectors[version] {
|
||||
for _, cc := range c.getCollectors() {
|
||||
cc.Collect(ch)
|
||||
}
|
||||
}
|
||||
|
109
version/version.go
Normal file
109
version/version.go
Normal file
@ -0,0 +1,109 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version contains all the Ceph version details
|
||||
type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
Revision int
|
||||
Commit string
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidVersion indicates that the given version string was invalid
|
||||
ErrInvalidVersion = errors.New("invalid version")
|
||||
|
||||
// Nautilus is the *Version at which Ceph nautilus was released
|
||||
Nautilus = &Version{Major: 14, Minor: 2, Patch: 0, Revision: 0, Commit: ""}
|
||||
|
||||
// Octopus is the *Version at which Ceph octopus was released
|
||||
Octopus = &Version{Major: 15, Minor: 2, Patch: 0, Revision: 0, Commit: ""}
|
||||
|
||||
// Pacific is the *Version at which Ceph pacific was released
|
||||
Pacific = &Version{Major: 16, Minor: 2, Patch: 0, Revision: 0, Commit: ""}
|
||||
)
|
||||
|
||||
// IsAtLeast returns true if the version is at least as new as the given constraint
|
||||
// the commit is not considered
|
||||
func (version *Version) IsAtLeast(constraint *Version) bool {
|
||||
if version.Major > constraint.Major {
|
||||
return true
|
||||
} else if version.Major < constraint.Major {
|
||||
return false
|
||||
}
|
||||
|
||||
if version.Minor > constraint.Minor {
|
||||
return true
|
||||
} else if version.Minor < constraint.Minor {
|
||||
return false
|
||||
}
|
||||
|
||||
if version.Patch > constraint.Patch {
|
||||
return true
|
||||
} else if version.Patch < constraint.Patch {
|
||||
return false
|
||||
}
|
||||
|
||||
if version.Revision > constraint.Revision {
|
||||
return true
|
||||
} else if version.Revision < constraint.Revision {
|
||||
return false
|
||||
}
|
||||
|
||||
// the versions must be the same
|
||||
return true
|
||||
}
|
||||
|
||||
// ParseCephVersion parses the given ceph version string to a *Version or error
|
||||
func ParseCephVersion(cephVersion string) (*Version, error) {
|
||||
splitVersion := strings.Split(cephVersion, " ")
|
||||
if len(splitVersion) < 3 {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
|
||||
someVersions := strings.Split(splitVersion[2], ".")
|
||||
if len(someVersions) != 3 {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
|
||||
otherVersions := strings.Split(someVersions[2], "-")
|
||||
if len(otherVersions) != 3 {
|
||||
return nil, ErrInvalidVersion
|
||||
}
|
||||
|
||||
major, err := strconv.Atoi(someVersions[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(someVersions[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patch, err := strconv.Atoi(otherVersions[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
revision, err := strconv.Atoi(otherVersions[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit := otherVersions[2]
|
||||
|
||||
return &Version{
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Patch: patch,
|
||||
Revision: revision,
|
||||
Commit: commit,
|
||||
}, nil
|
||||
}
|
125
version/version_test.go
Normal file
125
version/version_test.go
Normal file
@ -0,0 +1,125 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseCephVersion(t *testing.T) {
|
||||
type args struct {
|
||||
cephVersion string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Version
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid version 1",
|
||||
args: args{cephVersion: "totally real version"},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid version 2",
|
||||
args: args{cephVersion: "ceph version 14.2.18-97"},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "nautilus",
|
||||
args: args{cephVersion: "ceph version 14.2.18-97-gcc1e126 (cc1e1267bc7afc8288c718fc3e59c5a6735f6f4a) nautilus (stable)"},
|
||||
want: &Version{Major: 14, Minor: 2, Patch: 18, Revision: 97, Commit: "gcc1e126"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "octopus",
|
||||
args: args{cephVersion: "ceph version 15.2.0-1-gcc1e126"},
|
||||
want: &Version{Major: 15, Minor: 2, Patch: 0, Revision: 1, Commit: "gcc1e126"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "pacific",
|
||||
args: args{cephVersion: "ceph version 16.2.3-33-gcc1e126"},
|
||||
want: &Version{Major: 16, Minor: 2, Patch: 3, Revision: 33, Commit: "gcc1e126"},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseCephVersion(tt.args.cephVersion)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseCephVersion() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ParseCephVersion() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion_IsAtLeast(t *testing.T) {
|
||||
type fields struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
Revision int
|
||||
Commit string
|
||||
}
|
||||
type args struct {
|
||||
constraint *Version
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "equal versions",
|
||||
fields: fields{Major: Nautilus.Major, Minor: Nautilus.Minor, Patch: Nautilus.Patch, Revision: Nautilus.Revision, Commit: Nautilus.Commit},
|
||||
args: args{constraint: Nautilus},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "slightly older",
|
||||
fields: fields{Major: Pacific.Major, Minor: Pacific.Minor - 1, Patch: Pacific.Patch, Revision: Pacific.Revision, Commit: Pacific.Commit},
|
||||
args: args{constraint: Pacific},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "significantly newer",
|
||||
fields: fields{Major: Pacific.Major, Minor: Pacific.Minor - 1, Patch: Pacific.Patch, Revision: Pacific.Revision, Commit: Pacific.Commit},
|
||||
args: args{constraint: Octopus},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "older revision",
|
||||
fields: fields{Major: 16, Minor: 2, Patch: 0, Revision: 1},
|
||||
args: args{constraint: &Version{Major: 16, Minor: 2, Patch: 0, Revision: 2}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "newer revision",
|
||||
fields: fields{Major: 16, Minor: 2, Patch: 0, Revision: 1},
|
||||
args: args{constraint: Pacific},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
version := &Version{
|
||||
Major: tt.fields.Major,
|
||||
Minor: tt.fields.Minor,
|
||||
Patch: tt.fields.Patch,
|
||||
Revision: tt.fields.Revision,
|
||||
Commit: tt.fields.Commit,
|
||||
}
|
||||
if got := version.IsAtLeast(tt.args.constraint); got != tt.want {
|
||||
t.Errorf("IsAtLeast() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user