node_exporter/vendor/github.com/prometheus/procfs/btrfs/get.go

252 lines
6.6 KiB
Go

// 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
}