Commit fb649a79 authored by Olivier Bourdon's avatar Olivier Bourdon

Add option for excluding mounpoints by their names

parent 385c9e7a
......@@ -73,6 +73,10 @@ var (
"device_name",
"device_type",
}
dfltExcludedFSNames = []string{
"/proc/sys/fs/binfmt_misc",
"/var/lib/docker/aufs",
}
dfltExcludedFSTypes = []string{
"proc",
"binfmt_misc",
......@@ -114,6 +118,16 @@ func (p *dfCollector) setProcPath(cfg interface{}) error {
}
p.proc_path = procPath.(string)
}
excludedFSNames, err := config.GetConfigItem(cfg, "excluded_fs_names")
if err == nil {
if len(excludedFSNames.(string)) > 0 {
p.excluded_fs_names = strings.Split(excludedFSNames.(string), ",")
} else {
p.excluded_fs_names = []string{}
}
} else {
p.excluded_fs_names = dfltExcludedFSNames
}
excludedFSTypes, err := config.GetConfigItem(cfg, "excluded_fs_types")
if err == nil {
if len(excludedFSTypes.(string)) > 0 {
......@@ -152,7 +166,7 @@ func (p *dfCollector) CollectMetrics(mts []plugin.MetricType) ([]plugin.MetricTy
}
metrics := []plugin.MetricType{}
curTime := time.Now()
dfms, err := p.stats.collect(p.proc_path, p.excluded_fs_types)
dfms, err := p.stats.collect(p.proc_path, p.excluded_fs_names, p.excluded_fs_types)
if err != nil {
return metrics, fmt.Errorf(fmt.Sprintf("Unable to collect metrics from df: %s", err))
}
......@@ -292,6 +306,8 @@ func (p *dfCollector) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
node := cpolicy.NewPolicyNode()
node.Add(rule)
cp.Add([]string{nsVendor, nsClass, PluginName}, node)
rule, _ = cpolicy.NewStringRule("excluded_fs_names", false, strings.Join(dfltExcludedFSNames, ","))
node.Add(rule)
rule, _ = cpolicy.NewStringRule("excluded_fs_types", false, strings.Join(dfltExcludedFSTypes, ","))
node.Add(rule)
return cp, nil
......@@ -306,6 +322,7 @@ func NewDfCollector() *dfCollector {
logger: logger,
initializedMutex: imutex,
proc_path: procPath,
excluded_fs_names: dfltExcludedFSNames,
excluded_fs_types: dfltExcludedFSTypes,
}
}
......@@ -328,6 +345,7 @@ type dfCollector struct {
stats collector
logger *log.Logger
proc_path string
excluded_fs_names []string
excluded_fs_types []string
}
......@@ -343,12 +361,12 @@ type dfMetric struct {
}
type collector interface {
collect(string, []string) ([]dfMetric, error)
collect(string, []string, []string) ([]dfMetric, error)
}
type dfStats struct{}
func (dfs *dfStats) collect(procPath string, excluded_fs_types []string) ([]dfMetric, error) {
func (dfs *dfStats) collect(procPath string, excluded_fs_names []string, excluded_fs_types []string) ([]dfMetric, error) {
dfms := []dfMetric{}
cpath := path.Join(procPath, "1", "mountinfo")
fh, err := os.Open(cpath)
......@@ -375,51 +393,56 @@ func (dfs *dfStats) collect(procPath string, excluded_fs_types []string) ([]dfMe
return nil, fmt.Errorf("Wrong format %d fields found on the right side instead of 7 min", len(rightFields))
}
// Keep only meaningfull filesystems
if excludedFSType(rightFields[0], excluded_fs_types) {
if excludedFSFromList(leftFields[4], excluded_fs_names) {
log.Debug(fmt.Sprintf("Ignoring mount point %s",
leftFields[4]))
continue
}
if excludedFSFromList(rightFields[0], excluded_fs_types) {
log.Debug(fmt.Sprintf("Ignoring mount point %s with FS type %s",
leftFields[4], rightFields[0]))
continue
}
var dfm dfMetric
dfm.Filesystem = rightFields[1]
dfm.FsType = rightFields[0]
dfm.UnchangedMountPoint = leftFields[4]
if leftFields[4] == "/" {
dfm.MountPoint = "rootfs"
} else {
var dfm dfMetric
dfm.Filesystem = rightFields[1]
dfm.FsType = rightFields[0]
dfm.UnchangedMountPoint = leftFields[4]
if leftFields[4] == "/" {
dfm.MountPoint = "rootfs"
} else {
dfm.MountPoint = strings.Replace(leftFields[4][1:], "/", "_", -1)
// Because there are mounted FS containing dots
// (like /etc/resolv.conf in Docker containers)
// and this is incompatible with Snap metric name policies
dfm.MountPoint = strings.Replace(dfm.MountPoint, ".", "_", -1)
}
stat := syscall.Statfs_t{}
err := syscall.Statfs(leftFields[4], &stat)
if err != nil {
log.Error(fmt.Sprintf("Error getting filesystem infos for %s", leftFields[4]))
continue
}
// Blocks
dfm.Blocks = (stat.Blocks * uint64(stat.Bsize)) / 1024
dfm.Available = (stat.Bavail * uint64(stat.Bsize)) / 1024
xFree := (stat.Bfree * uint64(stat.Bsize)) / 1024
dfm.Used = dfm.Blocks - xFree
percentAvailable := ceilPercent(dfm.Used, dfm.Used+dfm.Available)
dfm.Capacity = percentAvailable / 100.0
// Inodes
dfm.Inodes = stat.Files
dfm.IFree = stat.Ffree
dfm.IUsed = dfm.Inodes - dfm.IFree
percentIUsed := ceilPercent(dfm.IUsed, dfm.Inodes)
dfm.IUse = percentIUsed / 100.0
dfms = append(dfms, dfm)
dfm.MountPoint = strings.Replace(leftFields[4][1:], "/", "_", -1)
// Because there are mounted FS containing dots
// (like /etc/resolv.conf in Docker containers)
// and this is incompatible with Snap metric name policies
dfm.MountPoint = strings.Replace(dfm.MountPoint, ".", "_", -1)
}
stat := syscall.Statfs_t{}
err := syscall.Statfs(leftFields[4], &stat)
if err != nil {
log.Error(fmt.Sprintf("Error getting filesystem infos for %s", leftFields[4]))
continue
}
// Blocks
dfm.Blocks = (stat.Blocks * uint64(stat.Bsize)) / 1024
dfm.Available = (stat.Bavail * uint64(stat.Bsize)) / 1024
xFree := (stat.Bfree * uint64(stat.Bsize)) / 1024
dfm.Used = dfm.Blocks - xFree
percentAvailable := ceilPercent(dfm.Used, dfm.Used+dfm.Available)
dfm.Capacity = percentAvailable / 100.0
// Inodes
dfm.Inodes = stat.Files
dfm.IFree = stat.Ffree
dfm.IUsed = dfm.Inodes - dfm.IFree
percentIUsed := ceilPercent(dfm.IUsed, dfm.Inodes)
dfm.IUse = percentIUsed / 100.0
dfms = append(dfms, dfm)
}
return dfms, nil
}
// Return true if filesystem should not be taken into account
func excludedFSType(fs string, excludedFSTypes []string) bool {
for _, v := range excludedFSTypes {
func excludedFSFromList(fs string, excludedFS []string) bool {
for _, v := range excludedFS {
if fs == v {
return true
}
......
......@@ -41,8 +41,6 @@ type DfPluginSuite struct {
}
func (dfp *DfPluginSuite) SetupSuite() {
mc := &MockCollector{}
dfms := []dfMetric{
dfMetric{
Blocks: 100,
......@@ -71,8 +69,9 @@ func (dfp *DfPluginSuite) SetupSuite() {
IUse: 0.5,
},
}
mc.On("collect", "/proc", dfltExcludedFSTypes).Return(dfms, nil)
mc.On("collect", "/dummy", dfltExcludedFSTypes).Return(dfms, errors.New("Fake error"))
mc := &MockCollector{}
mc.On("collect", "/proc", dfltExcludedFSNames, dfltExcludedFSTypes).Return(dfms, nil)
mc.On("collect", "/dummy", dfltExcludedFSNames, dfltExcludedFSTypes).Return(dfms, errors.New("Fake error"))
dfp.mockCollector = mc
dfp.cfg = plugin.ConfigType{}
}
......@@ -359,8 +358,7 @@ func (dfp *DfPluginSuite) TestCollect() {
dfPlg := NewDfCollector()
Convey("When called with non existing path", func() {
metrics, err := dfPlg.stats.collect("/dummy", []string{})
metrics, err := dfPlg.stats.collect("/dummy", []string{}, []string{})
Convey("Then error should be reported", func() {
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
......@@ -368,12 +366,29 @@ func (dfp *DfPluginSuite) TestCollect() {
})
})
Convey("When called with existing path", func() {
metrics, err := dfPlg.stats.collect("/proc", []string{})
Convey("When called with existing path and different exclusion lists", func() {
metrics, err := dfPlg.stats.collect("/proc", []string{"dummy"}, []string{"dummy"})
Convey("Then no error should be reported with dummy exclusion lists", func() {
So(err, ShouldBeNil)
So(metrics, ShouldNotBeNil)
})
nbMetrics := len(metrics)
exclusions := false
for _, m := range metrics {
if excludedFSFromList(m.UnchangedMountPoint, dfltExcludedFSNames) ||
excludedFSFromList(m.FsType, dfltExcludedFSTypes) {
exclusions = true
}
}
Convey("Then no exclusion occured", func() {
So(exclusions, ShouldEqual, true)
})
metrics, err = dfPlg.stats.collect("/proc", dfltExcludedFSNames, dfltExcludedFSTypes)
Convey("Then error should be reported", func() {
So(err, ShouldBeNil)
So(metrics, ShouldNotBeNil)
So(len(metrics), ShouldBeLessThan, nbMetrics)
})
})
})
......@@ -437,6 +452,31 @@ func (dfp *DfPluginSuite) TestHelperRoutines() {
Convey("Then no error should be reported (already called)", func() {
So(err, ShouldBeNil)
So(len(dfPlg.excluded_fs_types), ShouldEqual, 18)
So(len(dfPlg.excluded_fs_names), ShouldEqual, 2)
})
node = cdata.NewNode()
node.AddItem("excluded_fs_names", ctypes.ConfigValueStr{Value: ""})
dfPlg = NewDfCollector()
dfPlg.stats = dfp.mockCollector
cfg = plugin.ConfigType{ConfigDataNode: node}
err = dfPlg.setProcPath(cfg)
Convey("Then no error should be reported (excluded_fs_names with proper value for empty string)", func() {
So(err, ShouldBeNil)
So(len(dfPlg.excluded_fs_names), ShouldEqual, 0)
})
node = cdata.NewNode()
node.AddItem("excluded_fs_names", ctypes.ConfigValueStr{Value: "n1,n2,n3"})
dfPlg = NewDfCollector()
dfPlg.stats = dfp.mockCollector
cfg = plugin.ConfigType{ConfigDataNode: node}
err = dfPlg.setProcPath(cfg)
Convey("Then no error should be reported (excluded_fs_names with proper value)", func() {
So(err, ShouldBeNil)
So(len(dfPlg.excluded_fs_names), ShouldEqual, 3)
})
node = cdata.NewNode()
......@@ -494,13 +534,13 @@ func (dfp *DfPluginSuite) TestHelperRoutines() {
})
l := []string{"1", "2", "3", "4"}
v = excludedFSType("3", l)
v = excludedFSFromList("3", l)
Convey("Then value should be reported as found", func() {
So(v, ShouldEqual, true)
})
v = excludedFSType("error", l)
v = excludedFSFromList("error", l)
Convey("Then value should be reported as not found", func() {
So(v, ShouldEqual, false)
......@@ -538,7 +578,7 @@ type MockCollector struct {
mock.Mock
}
func (mc *MockCollector) collect(p string, i []string) ([]dfMetric, error) {
ret := mc.Mock.Called(p, i)
func (mc *MockCollector) collect(p string, n []string, f []string) ([]dfMetric, error) {
ret := mc.Mock.Called(p, n, f)
return ret.Get(0).([]dfMetric), ret.Error(1)
}
......@@ -17,6 +17,7 @@
"config": {
"/intel/procfs/filesystem": {
"proc_path": "/proc",
"excluded_fs_names": "/proc/sys/fs/binfmt_misc,/var/lib/docker/aufs",
"excluded_fs_types": "proc,binfmt_misc,fuse.gvfsd-fuse,sysfs,cgroup,fusectl,pstore,debugfs,securityfs,devpts,mqueue,hugetlbfs,nsfs,rpc_pipefs,devtmpfs,none,tmpfs,aufs"
}
},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment