Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #12 from insoIite/feature/add_quota_metrics
Browse files Browse the repository at this point in the history
Feature/add quota metrics
  • Loading branch information
ofesseler authored Apr 25, 2017
2 parents 9bad235 + 69398e7 commit 250ac73
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 4 deletions.
17 changes: 17 additions & 0 deletions gluster_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,20 @@ func ExecVolumeHealInfo(volumeName string) (int, error) {
}
return entriesOutOfSync, nil
}

// ExecVolumeQuotaList executes volume quota list on host system and processess input
// returns QuotaList structs and errors

func ExecVolumeQuotaList(volumeName string) (structs.VolumeQuotaXML, error) {
args := []string{"volume", "quota", volumeName, "list"}
bytesBuffer, cmdErr := execGlusterCommand(args...)
if cmdErr != nil {
return structs.VolumeQuotaXML{}, cmdErr
}
volumeQuota, err := structs.VolumeQuotaListXMLUnmarshall(bytesBuffer)
if err != nil {
log.Errorf("Something went wrong while unmarshalling xml: %v", err)
return volumeQuota, err
}
return volumeQuota, nil
}
115 changes: 111 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,36 @@ var (
prometheus.BuildFQName(namespace, "", "mount_successful"),
"Checks if mountpoint exists, returns a bool value 0 or 1",
[]string{"volume", "mountpoint"}, nil)

quotaHardLimit = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_hardlimit"),
"Quota hard limit (bytes) in a volume",
[]string{"path", "volume"}, nil)

quotaSoftLimit = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_softlimit"),
"Quota soft limit (bytes) in a volume",
[]string{"path", "volume"}, nil)

quotaUsed = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_used"),
"Current data (bytes) used in a quota",
[]string{"path", "volume"}, nil)

quotaAvailable = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_available"),
"Current data (bytes) available in a quota",
[]string{"path", "volume"}, nil)

quotaSoftLimitExceeded = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_softlimit_exceeded"),
"Is the quota soft-limit exceeded",
[]string{"path", "volume"}, nil)

quotaHardLimitExceeded = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "volume_quota_hardlimit_exceeded"),
"Is the quota hard-limit exceeded",
[]string{"path", "volume"}, nil)
)

// Exporter holds name, path and volumes to be monitored
Expand All @@ -142,6 +172,7 @@ type Exporter struct {
path string
volumes []string
profile bool
quota bool
}

// Describe all the metrics exported by Gluster exporter. It implements prometheus.Collector.
Expand All @@ -163,6 +194,12 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- healInfoFilesCount
ch <- volumeWriteable
ch <- mountSuccessful
ch <- quotaHardLimit
ch <- quotaSoftLimit
ch <- quotaUsed
ch <- quotaAvailable
ch <- quotaSoftLimitExceeded
ch <- quotaHardLimitExceeded
}

// Collect collects all the metrics
Expand Down Expand Up @@ -257,7 +294,6 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
)
}
}

}
}
}
Expand Down Expand Up @@ -336,6 +372,75 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
}
}
}
if e.quota {
for _, volume := range volumeInfo.VolInfo.Volumes.Volume {
if e.volumes[0] == allVolumes || ContainsVolume(e.volumes, volume.Name) {
volumeQuotaXML, err := ExecVolumeQuotaList(volume.Name)
if err != nil {
log.Error("Cannot create quota metrics if quotas are not enabled in your gluster server")
} else {
for _, limit := range volumeQuotaXML.VolQuota.QuotaLimits {
ch <- prometheus.MustNewConstMetric(
quotaHardLimit,
prometheus.CounterValue,
float64(limit.HardLimit),
limit.Path,
volume.Name,
)

ch <- prometheus.MustNewConstMetric(
quotaSoftLimit,
prometheus.CounterValue,
float64(limit.SoftLimitValue),
limit.Path,
volume.Name,
)
ch <- prometheus.MustNewConstMetric(
quotaUsed,
prometheus.CounterValue,
float64(limit.UsedSpace),
limit.Path,
volume.Name,
)

ch <- prometheus.MustNewConstMetric(
quotaAvailable,
prometheus.CounterValue,
float64(limit.AvailSpace),
limit.Path,
volume.Name,
)

var slExceeded float64
slExceeded = 0.0
if limit.SlExceeded != "No" {
slExceeded = 1.0
}
ch <- prometheus.MustNewConstMetric(
quotaSoftLimitExceeded,
prometheus.CounterValue,
slExceeded,
limit.Path,
volume.Name,
)

var hlExceeded float64
hlExceeded = 0.0
if limit.HlExceeded != "No" {
hlExceeded = 1.0
}
ch <- prometheus.MustNewConstMetric(
quotaHardLimitExceeded,
prometheus.CounterValue,
hlExceeded,
limit.Path,
volume.Name,
)
}
}
}
}
}
}

type mount struct {
Expand All @@ -357,7 +462,7 @@ func parseMountOutput(mountBuffer string) ([]mount, error) {
return mounts, nil
}

// ContainsVolume checks a slice if it cpntains a element
// ContainsVolume checks a slice if it contains an element
func ContainsVolume(slice []string, element string) bool {
for _, a := range slice {
if a == element {
Expand All @@ -368,7 +473,7 @@ func ContainsVolume(slice []string, element string) bool {
}

// NewExporter initialises exporter
func NewExporter(hostname, glusterExecPath, volumesString string, profile bool) (*Exporter, error) {
func NewExporter(hostname, glusterExecPath, volumesString string, profile bool, quota bool) (*Exporter, error) {
if len(glusterExecPath) < 1 {
log.Fatalf("Gluster executable path is wrong: %v", glusterExecPath)
}
Expand All @@ -382,6 +487,7 @@ func NewExporter(hostname, glusterExecPath, volumesString string, profile bool)
path: glusterExecPath,
volumes: volumes,
profile: profile,
quota: quota,
}, nil
}

Expand All @@ -404,6 +510,7 @@ func main() {
showVersion = flag.Bool("version", false, "Prints version information")
glusterVolumes = flag.String("volumes", allVolumes, fmt.Sprintf("Comma separated volume names: vol1,vol2,vol3. Default is '%v' to scrape all metrics", allVolumes))
profile = flag.Bool("profile", false, "When profiling reports in gluster are enabled, set ' -profile true' to get more metrics")
quota = flag.Bool("quota", false, "When quota in gluster are enabled, set ' -quota true' to get more metrics")
)
flag.Parse()

Expand All @@ -415,7 +522,7 @@ func main() {
if err != nil {
log.Fatalf("While trying to get Hostname error happened: %v", err)
}
exporter, err := NewExporter(hostname, *glusterPath, *glusterVolumes, *profile)
exporter, err := NewExporter(hostname, *glusterPath, *glusterVolumes, *profile, *quota)
if err != nil {
log.Errorf("Creating new Exporter went wrong, ... \n%v", err)
}
Expand Down
35 changes: 35 additions & 0 deletions structs/xmlStructs.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,38 @@ func VolumeStatusAllDetailXMLUnmarshall(cmdOutBuff *bytes.Buffer) (VolumeStatusX
xml.Unmarshal(b, &vol)
return vol, nil
}

type QuotaLimit struct {
XMLName xml.Name `xml:"limit"`
Path string `xml:"path"`
HardLimit uint64 `xml:"hard_limit"`
SoftLimitValue uint64 `xml:"soft_limit_value"`
UsedSpace uint64 `xml:"used_space"`
AvailSpace uint64 `xml:"avail_space"`
SlExceeded string `xml:"sl_exceeded"`
HlExceeded string `xml:"hl_exceeded"`
}

type VolQuota struct {
XMLName xml.Name `xml:"volQuota"`
QuotaLimits []QuotaLimit `xml:"limit"`
}
// VolumeQuotaXML XML type of "gluster volume quota list"
type VolumeQuotaXML struct {
XMLName xml.Name `xml:"cliOutput"`
OpRet int `xml:"opRet"`
OpErrno int `xml:"opErrno"`
OpErrstr string `xml:"opErrstr"`
VolQuota VolQuota `xml:"volQuota"`
}

func VolumeQuotaListXMLUnmarshall(cmdOutBuff *bytes.Buffer) (VolumeQuotaXML, error) {
var volQuotaXML VolumeQuotaXML
b, err := ioutil.ReadAll(cmdOutBuff)
if err != nil {
log.Error(err)
return volQuotaXML, err
}
xml.Unmarshal(b, &volQuotaXML)
return volQuotaXML, nil
}
36 changes: 36 additions & 0 deletions structs/xmlStructs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,39 @@ func TestVolumeHealInfoXMLUnmarshall(t *testing.T) {
}

}

func TestVolumeQuotaListXMLUnmarshall(t *testing.T) {
testXMLPath := "../test/gluster_volume_quota_list.xml"
nodeCount := 2
dat, err := ioutil.ReadFile(testXMLPath)

if err != nil {
t.Errorf("error reading testxml in Path: %v", testXMLPath)
}
volumeQuotaXML, err := VolumeQuotaListXMLUnmarshall(bytes.NewBuffer(dat))
if err != nil {
t.Error(err)
}

if volumeQuotaXML.OpErrno != 0 {
t.Error(volumeQuotaXML.OpErrstr)
}
nb_limits := len(volumeQuotaXML.VolQuota.QuotaLimits)
if nb_limits != nodeCount {
t.Error("Expected %v Limits and len is %v", nodeCount, nb_limits)
}

for _, limit := range volumeQuotaXML.VolQuota.QuotaLimits {
if limit.Path == "/foo" {
if limit.AvailSpace != 10309258240 {
t.Error(
"Expected %v for available space in path %v, got %v",
1811939328,
limit.Path,
limit.AvailSpace,
)
}
}
}

}
27 changes: 27 additions & 0 deletions test/gluster_volume_quota_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<cliOutput>
<opRet>0</opRet>
<opErrno>0</opErrno>
<opErrstr/>
<volQuota>
<limit>
<path>/foo</path>
<hard_limit>10737418240</hard_limit>
<soft_limit_percent>80%</soft_limit_percent>
<soft_limit_value>8589934592</soft_limit_value>
<used_space>428160000</used_space>
<avail_space>10309258240</avail_space>
<sl_exceeded>No</sl_exceeded>
<hl_exceeded>No</hl_exceeded>
</limit>
<limit>
<path>/bar</path>
<hard_limit>2147483648</hard_limit>
<soft_limit_percent>80%</soft_limit_percent>
<soft_limit_value>1717986918</soft_limit_value>
<used_space>335544320</used_space>
<avail_space>1811939328</avail_space>
<sl_exceeded>No</sl_exceeded>
<hl_exceeded>No</hl_exceeded>
</limit>
</volQuota>
</cliOutput>

0 comments on commit 250ac73

Please sign in to comment.