Commit 8d7ba1df authored by Olivier Bourdon's avatar Olivier Bourdon

Fix for issue #10: Namespace ambiguity issue for filesystem mount point part of metrics

Add configurable option for keeping unchanged mountpoints
parent fe6d161b
......@@ -109,6 +109,7 @@ Create a task manifest file to use snap-plugin-collector-df plugin (exemplary fi
"config": {
"/intel/procfs/filesystem": {
"proc_path": "/proc",
"keep_original_mountpoint": true,
"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"
}
......
......@@ -45,15 +45,17 @@ const (
// PluginName df collector plugin name
PluginName = "df"
// Version of plugin
Version = 4
Version = 5
nsVendor = "intel"
nsClass = "procfs"
nsType = "filesystem"
ProcPath = "proc_path"
ExcludedFSNames = "excluded_fs_names"
ExcludedFSTypes = "excluded_fs_types"
ProcPath = "proc_path"
ExcludedFSNames = "excluded_fs_names"
ExcludedFSTypes = "excluded_fs_types"
KeepOriginalMountPoint = "keep_original_mountpoint"
MountInfoFile = "mountinfo"
)
var (
......@@ -142,6 +144,10 @@ func (p *dfCollector) setProcPath(cfg interface{}) error {
} else {
p.excluded_fs_types = dfltExcludedFSTypes
}
keepMount, err := config.GetConfigItem(cfg, KeepOriginalMountPoint)
if err == nil {
p.keep_original_mountpoint = keepMount.(bool)
}
p.initialized = true
return nil
}
......@@ -170,7 +176,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_names, p.excluded_fs_types)
dfms, err := p.stats.collect(p.proc_path, p.excluded_fs_names, p.excluded_fs_types, p.keep_original_mountpoint)
if err != nil {
return metrics, fmt.Errorf(fmt.Sprintf("Unable to collect metrics from df: %s", err))
}
......@@ -310,10 +316,12 @@ func (p *dfCollector) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
node := cpolicy.NewPolicyNode()
node.Add(rule)
cp.Add([]string{nsVendor, nsClass, PluginName}, node)
rule, _ = cpolicy.NewStringRule(ExcludedFSNames, false, strings.Join(dfltExcludedFSNames, ","))
node.Add(rule)
rule, _ = cpolicy.NewStringRule(ExcludedFSTypes, false, strings.Join(dfltExcludedFSTypes, ","))
node.Add(rule)
rule1, _ := cpolicy.NewStringRule(ExcludedFSNames, false, strings.Join(dfltExcludedFSNames, ","))
node.Add(rule1)
rule2, _ := cpolicy.NewStringRule(ExcludedFSTypes, false, strings.Join(dfltExcludedFSTypes, ","))
node.Add(rule2)
rule3, _ := cpolicy.NewBoolRule(KeepOriginalMountPoint, false, true)
node.Add(rule3)
return cp, nil
}
......@@ -322,12 +330,13 @@ func NewDfCollector() *dfCollector {
logger := log.New()
imutex := new(sync.Mutex)
return &dfCollector{
stats: &dfStats{},
logger: logger,
initializedMutex: imutex,
proc_path: procPath,
excluded_fs_names: dfltExcludedFSNames,
excluded_fs_types: dfltExcludedFSTypes,
stats: &dfStats{},
logger: logger,
initializedMutex: imutex,
proc_path: procPath,
excluded_fs_names: dfltExcludedFSNames,
excluded_fs_types: dfltExcludedFSTypes,
keep_original_mountpoint: true,
}
}
......@@ -345,13 +354,14 @@ func Meta() *plugin.PluginMeta {
}
type dfCollector struct {
initialized bool
initializedMutex *sync.Mutex
stats collector
logger *log.Logger
proc_path string
excluded_fs_names []string
excluded_fs_types []string
initialized bool
initializedMutex *sync.Mutex
stats collector
logger *log.Logger
proc_path string
excluded_fs_names []string
excluded_fs_types []string
keep_original_mountpoint bool
}
type dfMetric struct {
......@@ -366,14 +376,14 @@ type dfMetric struct {
}
type collector interface {
collect(string, []string, []string) ([]dfMetric, error)
collect(string, []string, []string, bool) ([]dfMetric, error)
}
type dfStats struct{}
func (dfs *dfStats) collect(procPath string, excluded_fs_names []string, excluded_fs_types []string) ([]dfMetric, error) {
func (dfs *dfStats) collect(procPath string, excluded_fs_names []string, excluded_fs_types []string, keep_original_mountpoint bool) ([]dfMetric, error) {
dfms := []dfMetric{}
cpath := path.Join(procPath, "1", "mountinfo")
cpath := path.Join(procPath, "1", MountInfoFile)
fh, err := os.Open(cpath)
if err != nil {
log.Error(fmt.Sprintf("Got error %#v", err))
......@@ -412,14 +422,18 @@ func (dfs *dfStats) collect(procPath string, excluded_fs_names []string, exclude
dfm.Filesystem = rightFields[1]
dfm.FsType = rightFields[0]
dfm.UnchangedMountPoint = leftFields[4]
if leftFields[4] == "/" {
dfm.MountPoint = "rootfs"
if keep_original_mountpoint {
dfm.MountPoint = leftFields[4]
} 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)
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)
......
......@@ -69,9 +69,39 @@ func (dfp *DfPluginSuite) SetupSuite() {
IUse: 0.5,
},
}
dfms_unchanged := []dfMetric{
dfMetric{
Blocks: 100,
Used: 50,
Available: 40,
Capacity: 0.5,
FsType: "ext4",
Filesystem: "/dev/sda1",
MountPoint: "/",
Inodes: 1000,
IUsed: 500,
IFree: 400,
IUse: 0.5,
},
dfMetric{
Blocks: 200,
Used: 110,
Available: 80,
Capacity: 0.3,
FsType: "ext4",
Filesystem: "/dev/sda2",
MountPoint: "/big",
Inodes: 2000,
IUsed: 1000,
IFree: 800,
IUse: 0.5,
},
}
mc := &MockCollector{}
mc.On("collect", "/proc", dfltExcludedFSNames, dfltExcludedFSTypes).Return(dfms, nil)
mc.On("collect", "/dummy", dfltExcludedFSNames, dfltExcludedFSTypes).Return(dfms, errors.New("Fake error"))
mc.On("collect", "/proc", dfltExcludedFSNames, dfltExcludedFSTypes, false).Return(dfms, nil)
mc.On("collect", "/dummy", dfltExcludedFSNames, dfltExcludedFSTypes, false).Return(dfms, errors.New("Fake error"))
mc.On("collect", "/proc", dfltExcludedFSNames, dfltExcludedFSTypes, true).Return(dfms_unchanged, nil)
mc.On("collect", "/dummy", dfltExcludedFSNames, dfltExcludedFSTypes, true).Return(dfms, errors.New("Fake error"))
dfp.mockCollector = mc
dfp.cfg = plugin.ConfigType{}
}
......@@ -172,12 +202,17 @@ func (dfp *DfPluginSuite) TestCollectMetrics() {
})
Convey("When list of specific metrics is requested", func() {
node := cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
mts := []plugin.MetricType{
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "rootfs", "space_free"),
Config_: node,
},
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "big", "inodes_percent_free"),
Config_: node,
},
}
metrics, err := dfPlg.CollectMetrics(mts)
......@@ -206,9 +241,12 @@ func (dfp *DfPluginSuite) TestCollectMetrics() {
})
Convey("When one single available dynamic metrics is requested", func() {
node := cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
mts := []plugin.MetricType{
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "*", "space_free"),
Config_: node,
},
}
metrics, err := dfPlg.CollectMetrics(mts)
......@@ -238,9 +276,12 @@ func (dfp *DfPluginSuite) TestCollectMetrics() {
})
Convey("When all available dynamic metrics are requested for given mountpoint", func() {
node := cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
mts := []plugin.MetricType{
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "rootfs", "*"),
Config_: node,
},
}
metrics, err := dfPlg.CollectMetrics(mts)
......@@ -266,9 +307,12 @@ func (dfp *DfPluginSuite) TestCollectMetrics() {
})
Convey("When all available dynamic metrics are requested for all mountpoints", func() {
node := cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
mts := []plugin.MetricType{
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "*", "*"),
Config_: node,
},
}
metrics, err := dfPlg.CollectMetrics(mts)
......@@ -298,9 +342,12 @@ func (dfp *DfPluginSuite) TestCollectMetrics() {
})
Convey("When list of all available dynamic metrics is requested", func() {
node := cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
mts := []plugin.MetricType{
plugin.MetricType{
Namespace_: core.NewNamespace("intel", "procfs", "filesystem", "*"),
Config_: node,
},
}
metrics, err := dfPlg.CollectMetrics(mts)
......@@ -358,7 +405,7 @@ func (dfp *DfPluginSuite) TestCollect() {
dfPlg := NewDfCollector()
Convey("When called with non existing path", func() {
metrics, err := dfPlg.stats.collect("/dummy", []string{}, []string{})
metrics, err := dfPlg.stats.collect("/dummy", []string{}, []string{}, false)
Convey("Then error should be reported", func() {
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
......@@ -367,7 +414,7 @@ func (dfp *DfPluginSuite) TestCollect() {
})
Convey("When called with existing path and different exclusion lists", func() {
metrics, err := dfPlg.stats.collect("/proc", []string{"dummy"}, []string{"dummy"})
metrics, err := dfPlg.stats.collect("/proc", []string{"dummy"}, []string{"dummy"}, false)
Convey("Then no error should be reported with dummy exclusion lists", func() {
So(err, ShouldBeNil)
So(metrics, ShouldNotBeNil)
......@@ -384,11 +431,33 @@ func (dfp *DfPluginSuite) TestCollect() {
So(exclusions, ShouldEqual, true)
})
metrics, err = dfPlg.stats.collect("/proc", dfltExcludedFSNames, dfltExcludedFSTypes)
metrics, err = dfPlg.stats.collect("/proc", dfltExcludedFSNames, dfltExcludedFSTypes, false)
Convey("Then error should be reported", func() {
So(err, ShouldBeNil)
So(metrics, ShouldNotBeNil)
So(len(metrics), ShouldBeLessThan, nbMetrics)
testMounts := false
for _, m := range metrics {
if m.MountPoint == m.UnchangedMountPoint {
testMounts = true
}
}
So(testMounts, ShouldEqual, false)
})
})
Convey("When called with existing path keeping original mount points", func() {
metrics, err := dfPlg.stats.collect("/proc", dfltExcludedFSNames, dfltExcludedFSTypes, true)
Convey("Then error should be reported", func() {
So(err, ShouldBeNil)
So(metrics, ShouldNotBeNil)
testMounts := false
for _, m := range metrics {
if m.MountPoint != m.UnchangedMountPoint {
testMounts = true
}
}
So(testMounts, ShouldEqual, false)
})
})
})
......@@ -502,6 +571,17 @@ func (dfp *DfPluginSuite) TestHelperRoutines() {
So(err, ShouldBeNil)
So(len(dfPlg.excluded_fs_types), ShouldEqual, 2)
})
node = cdata.NewNode()
node.AddItem(KeepOriginalMountPoint, ctypes.ConfigValueBool{Value: false})
cfg = plugin.ConfigType{ConfigDataNode: node}
dfPlg = NewDfCollector()
dfPlg.stats = dfp.mockCollector
err = dfPlg.setProcPath(cfg)
Convey("Then no error should be reported", func() {
So(err, ShouldBeNil)
})
})
Convey("Set get config policy", func() {
......@@ -578,7 +658,7 @@ type MockCollector struct {
mock.Mock
}
func (mc *MockCollector) collect(p string, n []string, f []string) ([]dfMetric, error) {
ret := mc.Mock.Called(p, n, f)
func (mc *MockCollector) collect(p string, n []string, f []string, b bool) ([]dfMetric, error) {
ret := mc.Mock.Called(p, n, f, b)
return ret.Get(0).([]dfMetric), ret.Error(1)
}
......@@ -17,6 +17,7 @@
"config": {
"/intel/procfs/filesystem": {
"proc_path": "/proc",
"keep_original_mountpoint": true,
"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