Commit 4c2e6715 authored by Steve Wills's avatar Steve Wills

first try

parent 6ff29866
// +build freebsd
/*
http://www.apache.org/licenses/LICENSE-2.0.txt
Copyright 2015 Intel Corporation
Copyright 2017 Steve Wills
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 df
/*
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
int mymode = MNT_NOWAIT;
struct statfs *fss;
int fscount;
void dfinit() {
fscount = getmntinfo(&fss, mymode);
}
int getfscount() {
return fscount;
}
char* getfsname(int fsnum) {
return fss[fsnum].f_mntonname;
}
char* getfsdevn(int fsnum) {
return fss[fsnum].f_mntfromname;
}
char* getfstype(int fsnum) {
return fss[fsnum].f_fstypename;
}
uint64_t getfsbsize(int fsnum) {
return fss[fsnum].f_bsize;
}
uint64_t getfsblocks(int fsnum) {
return fss[fsnum].f_blocks;
}
uint64_t getfsbfree(int fsnum) {
return fss[fsnum].f_bfree;
}
int64_t getfsbavail(int fsnum) {
return fss[fsnum].f_bavail;
}
uint64_t getfsfiles(int fsnum) {
return fss[fsnum].f_files;
}
int64_t getfsffree(int fsnum) {
return fss[fsnum].f_ffree;
}
*/
import "C"
import (
"fmt"
"strings"
"sync"
"time"
log "github.com/Sirupsen/logrus"
"github.com/intelsdi-x/snap/control/plugin"
"github.com/intelsdi-x/snap/control/plugin/cpolicy"
"github.com/intelsdi-x/snap/core"
)
const (
// PluginName df collector plugin name
PluginName = "df"
// Version of plugin
Version = 6
nsVendor = "swills"
nsClass = "fbsd"
nsType = "filesystem"
ExcludedFSNames = "excluded_fs_names"
ExcludedFSTypes = "excluded_fs_types"
)
var (
// prefix in metric namespace
namespacePrefix = []string{nsVendor, nsClass, nsType}
metricsKind = []string{
"space_free",
"space_reserved",
"space_used",
"space_percent_free",
"space_percent_reserved",
"space_percent_used",
"inodes_free",
"inodes_reserved",
"inodes_used",
"inodes_percent_free",
"inodes_percent_reserved",
"inodes_percent_used",
"device_name",
"device_type",
}
dfltExcludedFSNames = []string{
}
dfltExcludedFSTypes = []string{
"procfs",
"linprocfs",
}
)
// GetMetricTypes returns list of available metric types
// It returns error in case retrieval was not successful
func (p *dfCollector) GetMetricTypes(cfg plugin.ConfigType) ([]plugin.MetricType, error) {
mts := []plugin.MetricType{}
for _, kind := range metricsKind {
mts = append(mts, plugin.MetricType{
Namespace_: core.NewNamespace(namespacePrefix...).
AddDynamicElement(nsType, "name of filesystem").
AddStaticElement(kind),
Description_: "dynamic filesystem metric: " + kind,
})
}
return mts, nil
}
// CollectMetrics returns list of requested metric values
// It returns error in case retrieval was not successful
func (p *dfCollector) CollectMetrics(mts []plugin.MetricType) ([]plugin.MetricType, error) {
metrics := []plugin.MetricType{}
curTime := time.Now()
dfms, err := p.stats.collect(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))
}
for _, m := range mts {
ns := m.Namespace()
lns := len(ns)
if lns < 4 {
return nil, fmt.Errorf("Wrong namespace length %d: should be at least 4", lns)
}
// We can request all metrics for all devices in one shot
// using namespace /swills/fbsd/filesystem/*
if lns == 4 {
if ns[lns-1].Value != "*" {
return nil, fmt.Errorf("Namespace should contain wildcard")
}
for _, kind := range metricsKind {
for _, dfm := range dfms {
metric := createMetric(
core.NewNamespace(
createNamespace(dfm.MountPoint, kind)...),
curTime)
fillMetric(kind, dfm, &metric)
metrics = append(metrics, metric)
}
}
} else if ns[lns-2].Value == "*" {
// namespace /swills/fbsd/filesystem/*/<metric>
kind := ns[lns-1].Value
// <metric> is also wildcard => get them all
if kind == "*" {
for _, skind := range metricsKind {
for _, dfm := range dfms {
metric := createMetric(
core.NewNamespace(
createNamespace(dfm.MountPoint, skind)...),
curTime)
fillMetric(skind, dfm, &metric)
metrics = append(metrics, metric)
}
}
} else {
// <metric> is not wildcard => getonly matching metrics
for _, dfm := range dfms {
metric := createMetric(
core.NewNamespace(
createNamespace(dfm.MountPoint, kind)...),
curTime)
fillMetric(kind, dfm, &metric)
metrics = append(metrics, metric)
}
}
} else {
// namespace /swills/fbsd/filesystem/<fs>/<metric>
kind := ns[lns-1].Value
// <metric> is also wildcard => get them all
if kind == "*" {
for _, skind := range metricsKind {
for _, dfm := range dfms {
if ns[lns-2].Value == dfm.MountPoint {
metric := createMetric(
core.NewNamespace(
createNamespace(dfm.MountPoint, skind)...),
curTime)
fillMetric(skind, dfm, &metric)
metrics = append(metrics, metric)
}
}
}
} else {
for _, dfm := range dfms {
if ns[lns-2].Value == dfm.MountPoint {
metric := createMetric(ns, curTime)
fillMetric(kind, dfm, &metric)
metrics = append(metrics, metric)
}
}
}
}
}
return metrics, nil
}
func createMetric(ns core.Namespace, curTime time.Time) plugin.MetricType {
metric := plugin.MetricType{
Timestamp_: curTime,
Namespace_: ns,
}
ns[len(ns)-2].Name = nsType
return metric
}
// Function to fill metric with proper (computed) value
func fillMetric(kind string, dfm dfMetric, metric *plugin.MetricType) {
switch kind {
case "space_free":
metric.Data_ = dfm.Available
case "space_reserved":
metric.Data_ = dfm.Blocks - (dfm.Used + dfm.Available)
case "space_used":
metric.Data_ = dfm.Used
case "space_percent_free":
metric.Data_ = ceilPercent(dfm.Available, dfm.Blocks)
case "space_percent_reserved":
metric.Data_ = ceilPercent(dfm.Blocks-(dfm.Used+dfm.Available), dfm.Blocks)
case "space_percent_used":
metric.Data_ = ceilPercent(dfm.Used, dfm.Blocks)
case "device_name":
metric.Data_ = dfm.Filesystem
case "device_type":
metric.Data_ = dfm.FsType
case "inodes_free":
metric.Data_ = dfm.IFree
case "inodes_reserved":
metric.Data_ = dfm.Inodes - (dfm.IUsed + dfm.IFree)
case "inodes_used":
metric.Data_ = dfm.IUsed
case "inodes_percent_free":
metric.Data_ = ceilPercent(dfm.IFree, dfm.Inodes)
case "inodes_percent_reserved":
metric.Data_ = ceilPercent(dfm.Inodes-(dfm.IUsed+dfm.IFree), dfm.Inodes)
case "inodes_percent_used":
metric.Data_ = ceilPercent(dfm.IUsed, dfm.Inodes)
}
}
// createNamespace returns namespace slice of strings composed from: vendor, class, type and components of metric name
func createNamespace(elt string, name string) []string {
var suffix = []string{elt, name}
return append(namespacePrefix, suffix...)
}
// GetConfigPolicy returns config policy
// It returns error in case retrieval was not successful
func (p *dfCollector) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
cp := cpolicy.New()
node := cpolicy.NewPolicyNode()
cp.Add([]string{nsVendor, nsClass, PluginName}, node)
rule1, _ := cpolicy.NewStringRule(ExcludedFSNames, false, strings.Join(dfltExcludedFSNames, ","))
node.Add(rule1)
rule2, _ := cpolicy.NewStringRule(ExcludedFSTypes, false, strings.Join(dfltExcludedFSTypes, ","))
node.Add(rule2)
return cp, nil
}
// NewDfCollector creates new instance of plugin and returns pointer to initialized object.
func NewDfCollector() *dfCollector {
logger := log.New()
imutex := new(sync.Mutex)
return &dfCollector{
stats: &dfStats{},
logger: logger,
initializedMutex: imutex,
excluded_fs_names: dfltExcludedFSNames,
excluded_fs_types: dfltExcludedFSTypes,
}
}
// Meta returns plugin's metadata
func Meta() *plugin.PluginMeta {
return plugin.NewPluginMeta(
PluginName,
Version,
plugin.CollectorPluginType,
[]string{plugin.SnapGOBContentType},
[]string{plugin.SnapGOBContentType},
plugin.RoutingStrategy(plugin.StickyRouting),
plugin.ConcurrencyCount(1),
)
}
type dfCollector struct {
initialized bool
initializedMutex *sync.Mutex
stats collector
logger *log.Logger
excluded_fs_names []string
excluded_fs_types []string
}
type dfMetric struct {
Filesystem string
Used, Available, Blocks uint64
FsType string
MountPoint string
Inodes, IUsed, IFree uint64
}
type collector interface {
collect([]string, []string) ([]dfMetric, error)
}
type dfStats struct{}
func (dfs *dfStats) collect(excluded_fs_names []string, excluded_fs_types []string) ([]dfMetric, error) {
dfms := []dfMetric{}
C.dfinit()
fscount := int(C.getfscount());
for i := 0; i <= fscount; i++ {
fsname := C.GoString(C.getfsname(C.int(i)))
fstype := C.GoString(C.getfstype(C.int(i)))
fsdevn := C.GoString(C.getfsdevn(C.int(i)))
if excludedFSFromList(fsname, excluded_fs_names) {
log.Debug(fmt.Sprintf("Ignoring mount point %s", fsname))
continue
}
if excludedFSFromList(fstype, excluded_fs_types) {
log.Debug(fmt.Sprintf("Ignoring mount point %s with FS type %s", fsname, fstype))
continue
}
var dfm dfMetric
// Basics
dfm.Filesystem = fsdevn
dfm.FsType = fstype
dfm.MountPoint = fsname
// Blocks
dfm.Blocks = (uint64(C.getfsblocks(C.int(i))) * uint64(C.getfsbsize(C.int(i)))) / 1024
dfm.Available = (uint64(C.getfsbavail(C.int(i))) * uint64(C.getfsbsize(C.int(i)))) / 1024
xFree := (uint64(C.getfsbfree(C.int(i))) * uint64(C.getfsbsize(C.int(i)))) / 1024
dfm.Used = dfm.Blocks - xFree
// Inodes
dfm.Inodes = uint64(C.getfsfiles(C.int(i)))
dfm.IFree = uint64(C.getfsffree(C.int(i)))
dfm.IUsed = dfm.Inodes - dfm.IFree
dfms = append(dfms, dfm)
}
return dfms, nil
}
// Return true if filesystem should not be taken into account
func excludedFSFromList(fs string, excludeList []string) bool {
for _, v := range excludeList {
if fs == v {
return true
}
}
return false
}
// Ceiling function preventing addition of math library
func ceilPercent(v uint64, t uint64) float64 {
// Prevent division by 0 to occur
if t == 0 {
return 0.0
}
var v1i uint64
v1i = v * 100 / t
var v1f float64
v1f = float64(v) * 100.0 / float64(t)
var v2f float64
v2f = float64(v1i)
if v2f-1 < v1f && v1f <= v2f+1 {
addF := 0.0
if v2f < v1f {
addF = 1.0
}
v1f = v2f + addF
}
return v1f
}
......@@ -28,21 +28,34 @@ __proj_dir="$(dirname "$__dir")"
# shellcheck source=scripts/common.sh
. "${__dir}/common.sh"
UNAME=$(uname)
plugin_name=${__proj_dir##*/}
build_dir="${__proj_dir}/build"
go_build=(go build -ldflags "-w")
if [ ${UNAME} == "FreeBSD" ]; then
go_build=(go build -ldflags "-w" --ldflags '-linkmode external -extldflags "-static"')
else
go_build=(go build -ldflags "-w")
fi
_info "project path: ${__proj_dir}"
_info "plugin name: ${plugin_name}"
if [ ${UNAME} != "FreeBSD" ]; then
export CGO_ENABLED=0
fi
# rebuild binaries:
_debug "removing: ${build_dir:?}/*"
rm -rf "${build_dir:?}/"*
_info "building plugin: ${plugin_name}"
export GOOS=linux
if [ ${UNAME} == "FreeBSD" ]; then
export GOOS=freebsd
export CC=clang
else
export GOOS=linux
fi
export GOARCH=amd64
mkdir -p "${build_dir}/${GOOS}/x86_64"
"${go_build[@]}" -o "${build_dir}/${GOOS}/x86_64/${plugin_name}" . || exit 1
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