Add Btrfs collector (#1512)
* Add procfs/btrfs to vendor folder * Add Btrfs collector Resolves #1100 Signed-off-by: Silke Hofstra <silke@slxh.eu>pgier/disable-default-collectors
parent
ca1ac435ea
commit
8faa843fc4
@ -0,0 +1,189 @@ |
|||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !nobtrfs
|
||||||
|
|
||||||
|
package collector |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/go-kit/kit/log" |
||||||
|
"github.com/prometheus/client_golang/prometheus" |
||||||
|
"github.com/prometheus/procfs/btrfs" |
||||||
|
) |
||||||
|
|
||||||
|
// A btrfsCollector is a Collector which gathers metrics from Btrfs filesystems.
|
||||||
|
type btrfsCollector struct { |
||||||
|
fs btrfs.FS |
||||||
|
logger log.Logger |
||||||
|
} |
||||||
|
|
||||||
|
func init() { |
||||||
|
registerCollector("btrfs", defaultEnabled, NewBtrfsCollector) |
||||||
|
} |
||||||
|
|
||||||
|
// NewBtrfsCollector returns a new Collector exposing Btrfs statistics.
|
||||||
|
func NewBtrfsCollector(logger log.Logger) (Collector, error) { |
||||||
|
fs, err := btrfs.NewFS(*sysPath) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to open sysfs: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
return &btrfsCollector{ |
||||||
|
fs: fs, |
||||||
|
logger: logger, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Update retrieves and exports Btrfs statistics.
|
||||||
|
// It implements Collector.
|
||||||
|
func (c *btrfsCollector) Update(ch chan<- prometheus.Metric) error { |
||||||
|
stats, err := c.fs.Stats() |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("failed to retrieve Btrfs stats: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
for _, s := range stats { |
||||||
|
c.updateBtrfsStats(ch, s) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// btrfsMetric represents a single Btrfs metric that is converted into a Prometheus Metric.
|
||||||
|
type btrfsMetric struct { |
||||||
|
name string |
||||||
|
desc string |
||||||
|
value float64 |
||||||
|
extraLabel []string |
||||||
|
extraLabelValue []string |
||||||
|
} |
||||||
|
|
||||||
|
// updateBtrfsStats collects statistics for one bcache ID.
|
||||||
|
func (c *btrfsCollector) updateBtrfsStats(ch chan<- prometheus.Metric, s *btrfs.Stats) { |
||||||
|
const subsystem = "btrfs" |
||||||
|
|
||||||
|
// Basic information about the filesystem.
|
||||||
|
devLabels := []string{"uuid"} |
||||||
|
|
||||||
|
// Retrieve the metrics.
|
||||||
|
metrics := c.getMetrics(s) |
||||||
|
|
||||||
|
// Convert all gathered metrics to Prometheus Metrics and add to channel.
|
||||||
|
for _, m := range metrics { |
||||||
|
labels := append(devLabels, m.extraLabel...) |
||||||
|
|
||||||
|
desc := prometheus.NewDesc( |
||||||
|
prometheus.BuildFQName(namespace, subsystem, m.name), |
||||||
|
m.desc, |
||||||
|
labels, |
||||||
|
nil, |
||||||
|
) |
||||||
|
|
||||||
|
labelValues := []string{s.UUID} |
||||||
|
if len(m.extraLabelValue) > 0 { |
||||||
|
labelValues = append(labelValues, m.extraLabelValue...) |
||||||
|
} |
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric( |
||||||
|
desc, |
||||||
|
prometheus.GaugeValue, |
||||||
|
m.value, |
||||||
|
labelValues..., |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// getMetrics returns metrics for the given Btrfs statistics.
|
||||||
|
func (c *btrfsCollector) getMetrics(s *btrfs.Stats) []btrfsMetric { |
||||||
|
metrics := []btrfsMetric{ |
||||||
|
{ |
||||||
|
name: "info", |
||||||
|
desc: "Filesystem information", |
||||||
|
value: 1, |
||||||
|
extraLabel: []string{"label"}, |
||||||
|
extraLabelValue: []string{s.Label}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "global_rsv_size_bytes", |
||||||
|
desc: "Size of global reserve.", |
||||||
|
value: float64(s.Allocation.GlobalRsvSize), |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// Information about devices.
|
||||||
|
for n, dev := range s.Devices { |
||||||
|
metrics = append(metrics, btrfsMetric{ |
||||||
|
name: "device_size_bytes", |
||||||
|
desc: "Size of a device that is part of the filesystem.", |
||||||
|
value: float64(dev.Size), |
||||||
|
extraLabel: []string{"device"}, |
||||||
|
extraLabelValue: []string{n}, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// Information about data, metadata and system data.
|
||||||
|
metrics = append(metrics, c.getAllocationStats("data", s.Allocation.Data)...) |
||||||
|
metrics = append(metrics, c.getAllocationStats("metadata", s.Allocation.Metadata)...) |
||||||
|
metrics = append(metrics, c.getAllocationStats("system", s.Allocation.System)...) |
||||||
|
|
||||||
|
return metrics |
||||||
|
} |
||||||
|
|
||||||
|
// getAllocationStats returns allocation metrics for the given Btrfs Allocation statistics.
|
||||||
|
func (c *btrfsCollector) getAllocationStats(a string, s *btrfs.AllocationStats) []btrfsMetric { |
||||||
|
metrics := []btrfsMetric{ |
||||||
|
{ |
||||||
|
name: "reserved_bytes", |
||||||
|
desc: "Amount of space reserved for a data type", |
||||||
|
value: float64(s.ReservedBytes), |
||||||
|
extraLabel: []string{"block_group_type"}, |
||||||
|
extraLabelValue: []string{a}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// Add all layout statistics.
|
||||||
|
for layout, stats := range s.Layouts { |
||||||
|
metrics = append(metrics, c.getLayoutStats(a, layout, stats)...) |
||||||
|
} |
||||||
|
|
||||||
|
return metrics |
||||||
|
} |
||||||
|
|
||||||
|
// getLayoutStats returns metrics for a data layout.
|
||||||
|
func (c *btrfsCollector) getLayoutStats(a, l string, s *btrfs.LayoutUsage) []btrfsMetric { |
||||||
|
return []btrfsMetric{ |
||||||
|
{ |
||||||
|
name: "used_bytes", |
||||||
|
desc: "Amount of used space by a layout/data type", |
||||||
|
value: float64(s.UsedBytes), |
||||||
|
extraLabel: []string{"block_group_type", "mode"}, |
||||||
|
extraLabelValue: []string{a, l}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "size_bytes", |
||||||
|
desc: "Amount of space allocated for a layout/data type", |
||||||
|
value: float64(s.TotalBytes), |
||||||
|
extraLabel: []string{"block_group_type", "mode"}, |
||||||
|
extraLabelValue: []string{a, l}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "allocation_ratio", |
||||||
|
desc: "Data allocation ratio for a layout/data type", |
||||||
|
value: s.Ratio, |
||||||
|
extraLabel: []string{"block_group_type", "mode"}, |
||||||
|
extraLabelValue: []string{a, l}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,118 @@ |
|||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !nobtrfs
|
||||||
|
|
||||||
|
package collector |
||||||
|
|
||||||
|
import ( |
||||||
|
"strings" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/prometheus/procfs/btrfs" |
||||||
|
) |
||||||
|
|
||||||
|
var expectedBtrfsMetrics = [][]btrfsMetric{ |
||||||
|
{ |
||||||
|
{name: "info", value: 1, extraLabel: []string{"label"}, extraLabelValue: []string{"fixture"}}, |
||||||
|
{name: "global_rsv_size_bytes", value: 1.6777216e+07}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop25"}}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop26"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"data"}}, |
||||||
|
{name: "used_bytes", value: 8.08189952e+08, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid0"}}, |
||||||
|
{name: "size_bytes", value: 2.147483648e+09, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid0"}}, |
||||||
|
{name: "allocation_ratio", value: 1, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid0"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"metadata"}}, |
||||||
|
{name: "used_bytes", value: 933888, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid1"}}, |
||||||
|
{name: "size_bytes", value: 1.073741824e+09, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid1"}}, |
||||||
|
{name: "allocation_ratio", value: 2, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid1"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"system"}}, |
||||||
|
{name: "used_bytes", value: 16384, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid1"}}, |
||||||
|
{name: "size_bytes", value: 8.388608e+06, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid1"}}, |
||||||
|
{name: "allocation_ratio", value: 2, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid1"}}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
{name: "info", value: 1, extraLabel: []string{"label"}, extraLabelValue: []string{""}}, |
||||||
|
{name: "global_rsv_size_bytes", value: 1.6777216e+07}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop22"}}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop23"}}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop24"}}, |
||||||
|
{name: "device_size_bytes", value: 1.073741824e+10, extraLabel: []string{"device"}, extraLabelValue: []string{"loop25"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"data"}}, |
||||||
|
{name: "used_bytes", value: 0, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid5"}}, |
||||||
|
{name: "size_bytes", value: 6.44087808e+08, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid5"}}, |
||||||
|
{name: "allocation_ratio", value: 1.3333333333333333, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"data", "raid5"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"metadata"}}, |
||||||
|
{name: "used_bytes", value: 114688, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid6"}}, |
||||||
|
{name: "size_bytes", value: 4.29391872e+08, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid6"}}, |
||||||
|
{name: "allocation_ratio", value: 2, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"metadata", "raid6"}}, |
||||||
|
{name: "reserved_bytes", value: 0, extraLabel: []string{"block_group_type"}, extraLabelValue: []string{"system"}}, |
||||||
|
{name: "used_bytes", value: 16384, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid6"}}, |
||||||
|
{name: "size_bytes", value: 1.6777216e+07, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid6"}}, |
||||||
|
{name: "allocation_ratio", value: 2, extraLabel: []string{"block_group_type", "mode"}, extraLabelValue: []string{"system", "raid6"}}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func checkMetric(exp, got *btrfsMetric) bool { |
||||||
|
if exp.name != got.name || |
||||||
|
exp.value != got.value || |
||||||
|
len(exp.extraLabel) != len(got.extraLabel) || |
||||||
|
len(exp.extraLabelValue) != len(got.extraLabelValue) { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
for i := range exp.extraLabel { |
||||||
|
if exp.extraLabel[i] != got.extraLabel[i] { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// Devices (loopXX) can appear in random order, so just check the first 4 characters.
|
||||||
|
if strings.HasPrefix(got.extraLabelValue[i], "loop") && |
||||||
|
exp.extraLabelValue[i][:4] == got.extraLabelValue[i][:4] { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
if exp.extraLabelValue[i] != got.extraLabelValue[i] { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func TestBtrfs(t *testing.T) { |
||||||
|
fs, _ := btrfs.NewFS("fixtures/sys") |
||||||
|
collector := &btrfsCollector{fs: fs} |
||||||
|
|
||||||
|
stats, err := collector.fs.Stats() |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("Failed to retrieve Btrfs stats: %v", err) |
||||||
|
} |
||||||
|
if len(stats) != len(expectedBtrfsMetrics) { |
||||||
|
t.Fatalf("Unexpected number of Btrfs stats: expected %v, got %v", len(expectedBtrfsMetrics), len(stats)) |
||||||
|
} |
||||||
|
|
||||||
|
for i, s := range stats { |
||||||
|
metrics := collector.getMetrics(s) |
||||||
|
if len(metrics) != len(expectedBtrfsMetrics[i]) { |
||||||
|
t.Fatalf("Unexpected number of Btrfs metrics: expected %v, got %v", len(expectedBtrfsMetrics[i]), len(metrics)) |
||||||
|
} |
||||||
|
|
||||||
|
for j, m := range metrics { |
||||||
|
exp := expectedBtrfsMetrics[i][j] |
||||||
|
if !checkMetric(&exp, &m) { |
||||||
|
t.Errorf("Incorrect btrfs metric: expected %#v, got: %#v", exp, m) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package btrfs provides access to statistics exposed by Btrfs filesystems.
|
||||||
|
package btrfs |
||||||
|
|
||||||
|
// Stats contains statistics for a single Btrfs filesystem.
|
||||||
|
// See Linux fs/btrfs/sysfs.c for more information.
|
||||||
|
type Stats struct { |
||||||
|
UUID, Label string |
||||||
|
Allocation Allocation |
||||||
|
Devices map[string]*Device |
||||||
|
Features []string |
||||||
|
CloneAlignment uint64 |
||||||
|
NodeSize uint64 |
||||||
|
QuotaOverride uint64 |
||||||
|
SectorSize uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// Allocation contains allocation statistics for data, metadata and system data.
|
||||||
|
type Allocation struct { |
||||||
|
GlobalRsvReserved, GlobalRsvSize uint64 |
||||||
|
Data, Metadata, System *AllocationStats |
||||||
|
} |
||||||
|
|
||||||
|
// AllocationStats contains allocation statistics for a data type.
|
||||||
|
type AllocationStats struct { |
||||||
|
// Usage statistics
|
||||||
|
DiskUsedBytes uint64 |
||||||
|
DiskTotalBytes uint64 |
||||||
|
MayUseBytes uint64 |
||||||
|
PinnedBytes uint64 |
||||||
|
TotalPinnedBytes uint64 |
||||||
|
ReadOnlyBytes uint64 |
||||||
|
ReservedBytes uint64 |
||||||
|
UsedBytes uint64 |
||||||
|
TotalBytes uint64 |
||||||
|
|
||||||
|
// Flags marking filesystem state
|
||||||
|
// See Linux fs/btrfs/ctree.h for more information.
|
||||||
|
Flags uint64 |
||||||
|
|
||||||
|
// Additional disk usage statistics depending on the disk layout.
|
||||||
|
// At least one of these will exist and not be nil.
|
||||||
|
Layouts map[string]*LayoutUsage |
||||||
|
} |
||||||
|
|
||||||
|
// LayoutUsage contains additional usage statistics for a disk layout.
|
||||||
|
type LayoutUsage struct { |
||||||
|
UsedBytes, TotalBytes uint64 |
||||||
|
Ratio float64 |
||||||
|
} |
||||||
|
|
||||||
|
// Device contains information about a device that is part of a Btrfs filesystem.
|
||||||
|
type Device struct { |
||||||
|
Size uint64 |
||||||
|
} |
||||||
@ -0,0 +1,251 @@ |
|||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package btrfs |
||||||
|
|
||||||
|
import ( |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"path/filepath" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/prometheus/procfs/internal/fs" |
||||||
|
"github.com/prometheus/procfs/internal/util" |
||||||
|
) |
||||||
|
|
||||||
|
// SectorSize contains the Linux sector size.
|
||||||
|
// > Linux always considers sectors to be 512 bytes long independently
|
||||||
|
// > of the devices real block size.
|
||||||
|
const SectorSize = 512 |
||||||
|
|
||||||
|
// FS represents the pseudo-filesystem sys, which provides an interface to
|
||||||
|
// kernel data structures.
|
||||||
|
type FS struct { |
||||||
|
sys *fs.FS |
||||||
|
} |
||||||
|
|
||||||
|
// NewDefaultFS returns a new Bcache using the default sys fs mount point. It will error
|
||||||
|
// if the mount point can't be read.
|
||||||
|
func NewDefaultFS() (FS, error) { |
||||||
|
return NewFS(fs.DefaultSysMountPoint) |
||||||
|
} |
||||||
|
|
||||||
|
// NewFS returns a new Btrfs filesystem using the given sys fs mount point. It will error
|
||||||
|
// if the mount point can't be read.
|
||||||
|
func NewFS(mountPoint string) (FS, error) { |
||||||
|
if strings.TrimSpace(mountPoint) == "" { |
||||||
|
mountPoint = fs.DefaultSysMountPoint |
||||||
|
} |
||||||
|
sys, err := fs.NewFS(mountPoint) |
||||||
|
if err != nil { |
||||||
|
return FS{}, err |
||||||
|
} |
||||||
|
return FS{&sys}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Stats retrieves Btrfs filesystem runtime statistics for each mounted Btrfs filesystem.
|
||||||
|
func (fs FS) Stats() ([]*Stats, error) { |
||||||
|
matches, err := filepath.Glob(fs.sys.Path("fs/btrfs/*-*")) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
stats := make([]*Stats, 0, len(matches)) |
||||||
|
for _, uuidPath := range matches { |
||||||
|
s, err := GetStats(uuidPath) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// Set the UUID from the path when it could not be retrieved from the filesystem.
|
||||||
|
if s.UUID == "" { |
||||||
|
s.UUID = filepath.Base(uuidPath) |
||||||
|
} |
||||||
|
|
||||||
|
stats = append(stats, s) |
||||||
|
} |
||||||
|
|
||||||
|
return stats, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetStats collects all Btrfs statistics from sysfs
|
||||||
|
func GetStats(uuidPath string) (*Stats, error) { |
||||||
|
r := &reader{path: uuidPath} |
||||||
|
s := r.readFilesystemStats() |
||||||
|
if r.err != nil { |
||||||
|
return nil, r.err |
||||||
|
} |
||||||
|
|
||||||
|
return s, nil |
||||||
|
} |
||||||
|
|
||||||
|
type reader struct { |
||||||
|
path string |
||||||
|
err error |
||||||
|
devCount int |
||||||
|
} |
||||||
|
|
||||||
|
// readFile reads a file relative to the path of the reader.
|
||||||
|
// Non-existing files are ignored.
|
||||||
|
func (r *reader) readFile(n string) string { |
||||||
|
b, err := util.SysReadFile(path.Join(r.path, n)) |
||||||
|
if err != nil && !os.IsNotExist(err) { |
||||||
|
r.err = err |
||||||
|
} |
||||||
|
return strings.TrimSpace(string(b)) |
||||||
|
} |
||||||
|
|
||||||
|
// readValues reads a number of numerical values into an uint64 slice.
|
||||||
|
func (r *reader) readValue(n string) (v uint64) { |
||||||
|
// Read value from file
|
||||||
|
s := r.readFile(n) |
||||||
|
if r.err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Convert number
|
||||||
|
v, _ = strconv.ParseUint(s, 10, 64) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// listFiles returns a list of files for a directory of the reader.
|
||||||
|
func (r *reader) listFiles(p string) []string { |
||||||
|
files, err := ioutil.ReadDir(path.Join(r.path, p)) |
||||||
|
if err != nil { |
||||||
|
r.err = err |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
names := make([]string, len(files)) |
||||||
|
for i, f := range files { |
||||||
|
names[i] = f.Name() |
||||||
|
} |
||||||
|
return names |
||||||
|
} |
||||||
|
|
||||||
|
// readAllocationStats reads Btrfs allocation data for the current path.
|
||||||
|
func (r *reader) readAllocationStats(d string) (a *AllocationStats) { |
||||||
|
// Create a reader for this subdirectory
|
||||||
|
sr := &reader{path: path.Join(r.path, d), devCount: r.devCount} |
||||||
|
|
||||||
|
// Get the stats
|
||||||
|
a = &AllocationStats{ |
||||||
|
// Read basic allocation stats
|
||||||
|
MayUseBytes: sr.readValue("bytes_may_use"), |
||||||
|
PinnedBytes: sr.readValue("bytes_pinned"), |
||||||
|
ReadOnlyBytes: sr.readValue("bytes_readonly"), |
||||||
|
ReservedBytes: sr.readValue("bytes_reserved"), |
||||||
|
UsedBytes: sr.readValue("bytes_used"), |
||||||
|
DiskUsedBytes: sr.readValue("disk_used"), |
||||||
|
DiskTotalBytes: sr.readValue("disk_total"), |
||||||
|
Flags: sr.readValue("flags"), |
||||||
|
TotalBytes: sr.readValue("total_bytes"), |
||||||
|
TotalPinnedBytes: sr.readValue("total_bytes_pinned"), |
||||||
|
Layouts: sr.readLayouts(), |
||||||
|
} |
||||||
|
|
||||||
|
// Pass any error back
|
||||||
|
r.err = sr.err |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// readLayouts reads all Btrfs layout statistics for the current path
|
||||||
|
func (r *reader) readLayouts() map[string]*LayoutUsage { |
||||||
|
files, err := ioutil.ReadDir(r.path) |
||||||
|
if err != nil { |
||||||
|
r.err = err |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
m := make(map[string]*LayoutUsage) |
||||||
|
for _, f := range files { |
||||||
|
if f.IsDir() { |
||||||
|
m[f.Name()] = r.readLayout(f.Name()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return m |
||||||
|
} |
||||||
|
|
||||||
|
// readLayout reads the Btrfs layout statistics for an allocation layout.
|
||||||
|
func (r *reader) readLayout(p string) (l *LayoutUsage) { |
||||||
|
l = new(LayoutUsage) |
||||||
|
l.TotalBytes = r.readValue(path.Join(p, "total_bytes")) |
||||||
|
l.UsedBytes = r.readValue(path.Join(p, "used_bytes")) |
||||||
|
l.Ratio = r.calcRatio(p) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// calcRatio returns the calculated ratio for a layout mode.
|
||||||
|
func (r *reader) calcRatio(p string) float64 { |
||||||
|
switch p { |
||||||
|
case "single", "raid0": |
||||||
|
return 1 |
||||||
|
case "dup", "raid1", "raid10": |
||||||
|
return 2 |
||||||
|
case "raid5": |
||||||
|
return float64(r.devCount) / (float64(r.devCount) - 1) |
||||||
|
case "raid6": |
||||||
|
return float64(r.devCount) / (float64(r.devCount) - 2) |
||||||
|
default: |
||||||
|
return 0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// readDeviceInfo returns the information for all devices associated with this filesystem.
|
||||||
|
func (r *reader) readDeviceInfo(d string) map[string]*Device { |
||||||
|
devs := r.listFiles("devices") |
||||||
|
info := make(map[string]*Device, len(devs)) |
||||||
|
for _, n := range devs { |
||||||
|
info[n] = &Device{ |
||||||
|
Size: SectorSize * r.readValue("devices/"+n+"/size"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return info |
||||||
|
} |
||||||
|
|
||||||
|
// readFilesystemStats reads Btrfs statistics for a filesystem.
|
||||||
|
func (r *reader) readFilesystemStats() (s *Stats) { |
||||||
|
// First get disk info, and add it to reader
|
||||||
|
devices := r.readDeviceInfo("devices") |
||||||
|
r.devCount = len(devices) |
||||||
|
|
||||||
|
s = &Stats{ |
||||||
|
// Read basic filesystem information
|
||||||
|
Label: r.readFile("label"), |
||||||
|
UUID: r.readFile("metadata_uuid"), |
||||||
|
Features: r.listFiles("features"), |
||||||
|
CloneAlignment: r.readValue("clone_alignment"), |
||||||
|
NodeSize: r.readValue("nodesize"), |
||||||
|
QuotaOverride: r.readValue("quota_override"), |
||||||
|
SectorSize: r.readValue("sectorsize"), |
||||||
|
|
||||||
|
// Device info
|
||||||
|
Devices: devices, |
||||||
|
|
||||||
|
// Read allocation data
|
||||||
|
Allocation: Allocation{ |
||||||
|
GlobalRsvReserved: r.readValue("allocation/global_rsv_reserved"), |
||||||
|
GlobalRsvSize: r.readValue("allocation/global_rsv_size"), |
||||||
|
Data: r.readAllocationStats("allocation/data"), |
||||||
|
Metadata: r.readAllocationStats("allocation/metadata"), |
||||||
|
System: r.readAllocationStats("allocation/system"), |
||||||
|
}, |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
Loading…
Reference in new issue