Skip to content

Commit 28ecaa9

Browse files
committed
fs: introduce pluggable filesystem architecture
This change replaces hardcoded filesystem type handling in fs/fs.go with a pluggable system where each filesystem type is handled by a dedicated plugin. New plugin interfaces (fs/plugin.go): - FsPlugin: core interface for filesystem stats and mount processing - FsCachingPlugin: optional interface for plugins that cache stats by device ID (e.g., NFS with multiple mounts to same device) - FsWatcherPlugin: optional interface for background monitoring (e.g., ZFS watcher, ThinPool watcher) Filesystem plugins implemented: - fs/vfs: handles ext2/3/4, xfs, and standard block filesystems - fs/zfs: ZFS with optional watcher for container-level usage - fs/devicemapper: DeviceMapper thin pool support - fs/btrfs: btrfs with major/minor ID correction - fs/overlay: overlay with source uniqueness via device ID - fs/nfs: NFS with device-based caching - fs/tmpfs: tmpfs with per-mount tracking Signed-off-by: Davanum Srinivas <davanum@gmail.com>
1 parent 19a4038 commit 28ecaa9

File tree

24 files changed

+1614
-245
lines changed

24 files changed

+1614
-245
lines changed

cmd/internal/container/install/install.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ import (
2222
_ "github.com/google/cadvisor/container/docker/install"
2323
_ "github.com/google/cadvisor/container/podman/install"
2424
_ "github.com/google/cadvisor/container/systemd/install"
25+
26+
// Register all filesystem plugins.
27+
_ "github.com/google/cadvisor/fs/btrfs/install"
28+
_ "github.com/google/cadvisor/fs/devicemapper/install"
29+
_ "github.com/google/cadvisor/fs/nfs/install"
30+
_ "github.com/google/cadvisor/fs/overlay/install"
31+
_ "github.com/google/cadvisor/fs/tmpfs/install"
32+
_ "github.com/google/cadvisor/fs/vfs/install"
33+
_ "github.com/google/cadvisor/fs/zfs/install"
2534
)

fs/btrfs/install/install.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2014 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package install
16+
17+
import (
18+
"github.com/google/cadvisor/fs"
19+
"github.com/google/cadvisor/fs/btrfs"
20+
21+
"k8s.io/klog/v2"
22+
)
23+
24+
func init() {
25+
err := fs.RegisterPlugin("btrfs", btrfs.NewPlugin())
26+
if err != nil {
27+
klog.Fatalf("Failed to register btrfs fs plugin: %v", err)
28+
}
29+
}

fs/btrfs/mount.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2014 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build linux
16+
17+
package btrfs
18+
19+
import (
20+
"fmt"
21+
"syscall"
22+
23+
mount "github.com/moby/sys/mountinfo"
24+
"k8s.io/klog/v2"
25+
)
26+
27+
// major extracts the major device number from a device number.
28+
func major(devNumber uint64) uint {
29+
return uint((devNumber >> 8) & 0xfff)
30+
}
31+
32+
// minor extracts the minor device number from a device number.
33+
func minor(devNumber uint64) uint {
34+
return uint((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00))
35+
}
36+
37+
// GetBtrfsMajorMinorIds gets the major and minor device IDs for a btrfs mount point.
38+
// This is a workaround for wrong btrfs Major and Minor Ids reported in /proc/self/mountinfo.
39+
// Instead of using values from /proc/self/mountinfo we use stat to get Ids from btrfs mount point.
40+
func GetBtrfsMajorMinorIds(mnt *mount.Info) (int, int, error) {
41+
buf := new(syscall.Stat_t)
42+
err := syscall.Stat(mnt.Source, buf)
43+
if err != nil {
44+
err = fmt.Errorf("stat failed on %s with error: %s", mnt.Source, err)
45+
return 0, 0, err
46+
}
47+
48+
klog.V(4).Infof("btrfs mount %#v", mnt)
49+
if buf.Mode&syscall.S_IFMT == syscall.S_IFBLK {
50+
err := syscall.Stat(mnt.Mountpoint, buf)
51+
if err != nil {
52+
err = fmt.Errorf("stat failed on %s with error: %s", mnt.Mountpoint, err)
53+
return 0, 0, err
54+
}
55+
56+
// The type Dev and Rdev in Stat_t are 32bit on mips.
57+
klog.V(4).Infof("btrfs dev major:minor %d:%d\n", int(major(uint64(buf.Dev))), int(minor(uint64(buf.Dev)))) // nolint: unconvert
58+
klog.V(4).Infof("btrfs rdev major:minor %d:%d\n", int(major(uint64(buf.Rdev))), int(minor(uint64(buf.Rdev)))) // nolint: unconvert
59+
60+
return int(major(uint64(buf.Dev))), int(minor(uint64(buf.Dev))), nil // nolint: unconvert
61+
}
62+
return 0, 0, fmt.Errorf("%s is not a block device", mnt.Source)
63+
}

fs/btrfs/plugin.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2014 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build linux
16+
17+
package btrfs
18+
19+
import (
20+
"strings"
21+
22+
"github.com/google/cadvisor/fs"
23+
"github.com/google/cadvisor/fs/vfs"
24+
25+
mount "github.com/moby/sys/mountinfo"
26+
"k8s.io/klog/v2"
27+
)
28+
29+
type btrfsPlugin struct{}
30+
31+
// NewPlugin creates a new Btrfs filesystem plugin.
32+
func NewPlugin() fs.FsPlugin {
33+
return &btrfsPlugin{}
34+
}
35+
36+
func (p *btrfsPlugin) Name() string {
37+
return "btrfs"
38+
}
39+
40+
// CanHandle returns true if the filesystem type is btrfs.
41+
func (p *btrfsPlugin) CanHandle(fsType string) bool {
42+
return fsType == "btrfs"
43+
}
44+
45+
// Priority returns 100 - Btrfs has higher priority than VFS.
46+
func (p *btrfsPlugin) Priority() int {
47+
return 100
48+
}
49+
50+
// GetStats returns filesystem statistics for Btrfs.
51+
// Btrfs delegates to VFS for stats collection.
52+
func (p *btrfsPlugin) GetStats(device string, partition fs.PartitionInfo) (*fs.FsStats, error) {
53+
// Btrfs uses VFS stats
54+
capacity, free, avail, inodes, inodesFree, err := vfs.GetVfsStats(partition.Mountpoint)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
return &fs.FsStats{
60+
Capacity: capacity,
61+
Free: free,
62+
Available: avail,
63+
Inodes: &inodes,
64+
InodesFree: &inodesFree,
65+
Type: fs.VFS,
66+
}, nil
67+
}
68+
69+
// ProcessMount handles Btrfs mount processing.
70+
// Btrfs fix: following workaround fixes wrong btrfs Major and Minor Ids reported in /proc/self/mountinfo.
71+
// Instead of using values from /proc/self/mountinfo we use stat to get Ids from btrfs mount point.
72+
func (p *btrfsPlugin) ProcessMount(mnt *mount.Info) (bool, *mount.Info, error) {
73+
// Only apply fix if Major is 0 and Source starts with /dev/
74+
if mnt.Major == 0 && strings.HasPrefix(mnt.Source, "/dev/") {
75+
major, minor, err := GetBtrfsMajorMinorIds(mnt)
76+
if err != nil {
77+
klog.Warningf("%s", err)
78+
} else {
79+
// Create a copy with corrected values
80+
correctedMnt := *mnt
81+
correctedMnt.Major = major
82+
correctedMnt.Minor = minor
83+
return true, &correctedMnt, nil
84+
}
85+
}
86+
return true, mnt, nil
87+
}

fs/devicemapper/install/install.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2014 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package install
16+
17+
import (
18+
"github.com/google/cadvisor/fs"
19+
"github.com/google/cadvisor/fs/devicemapper"
20+
21+
"k8s.io/klog/v2"
22+
)
23+
24+
func init() {
25+
err := fs.RegisterPlugin("devicemapper", devicemapper.NewPlugin())
26+
if err != nil {
27+
klog.Fatalf("Failed to register devicemapper fs plugin: %v", err)
28+
}
29+
}

fs/devicemapper/plugin.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2014 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package devicemapper
16+
17+
import (
18+
"github.com/google/cadvisor/fs"
19+
20+
mount "github.com/moby/sys/mountinfo"
21+
"k8s.io/klog/v2"
22+
)
23+
24+
type dmPlugin struct{}
25+
26+
// NewPlugin creates a new DeviceMapper filesystem plugin.
27+
func NewPlugin() fs.FsPlugin {
28+
return &dmPlugin{}
29+
}
30+
31+
func (p *dmPlugin) Name() string {
32+
return "devicemapper"
33+
}
34+
35+
// CanHandle returns true if the filesystem type is devicemapper.
36+
func (p *dmPlugin) CanHandle(fsType string) bool {
37+
return fsType == "devicemapper"
38+
}
39+
40+
// Priority returns 100 - DeviceMapper has higher priority than VFS.
41+
func (p *dmPlugin) Priority() int {
42+
return 100
43+
}
44+
45+
// GetStats returns filesystem statistics for DeviceMapper thin provisioning.
46+
func (p *dmPlugin) GetStats(device string, partition fs.PartitionInfo) (*fs.FsStats, error) {
47+
capacity, free, avail, err := GetDMStats(device, partition.BlockSize)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
klog.V(5).Infof("got devicemapper fs capacity stats: capacity: %v free: %v available: %v", capacity, free, avail)
53+
54+
return &fs.FsStats{
55+
Capacity: capacity,
56+
Free: free,
57+
Available: avail,
58+
Type: fs.DeviceMapper,
59+
}, nil
60+
}
61+
62+
// ProcessMount handles DeviceMapper mount processing.
63+
// For DeviceMapper, no special processing is needed.
64+
func (p *dmPlugin) ProcessMount(mnt *mount.Info) (bool, *mount.Info, error) {
65+
return true, mnt, nil
66+
}

0 commit comments

Comments
 (0)