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