github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-inode/lib/inode.go (about)

     1  package mpinode
     2  
     3  import (
     4  	"os"
     5  	"os/exec"
     6  	"regexp"
     7  	"runtime"
     8  	"strconv"
     9  	"strings"
    10  
    11  	mp "github.com/mackerelio/go-mackerel-plugin-helper"
    12  	"github.com/mackerelio/golib/logging"
    13  )
    14  
    15  var logger = logging.GetLogger("metrics.plugin.inode")
    16  
    17  // InodePlugin plugin
    18  type InodePlugin struct{}
    19  
    20  var dfHeaderPattern = regexp.MustCompile(
    21  	`^Filesystem\s+`,
    22  )
    23  
    24  var dfColumnsPattern = regexp.MustCompile(
    25  	`^(.+?)\s+(?:(?:\d+)\s+(?:\d+)\s+(?:\d+)\s+(?:\d+%)\s+|(?:\d+)\s+)?(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$`,
    26  )
    27  
    28  var devicePattern = regexp.MustCompile(
    29  	`^/dev/(.*)$`,
    30  )
    31  
    32  var deviceUnacceptablePattern = regexp.MustCompile(
    33  	`[^A-Za-z0-9_-]`,
    34  )
    35  
    36  //  $ df -iP
    37  // Filesystem      Inodes  IUsed   IFree IUse% Mounted on
    38  // /dev/xvda1     1310720 131197 1179523   11% /
    39  //  $ df -i # on Mac OSX (impossible to display only inode information)
    40  // Filesystem 512-blocks      Used Available Capacity  iused    ifree %iused  Mounted on
    41  // /dev/disk1  974737408 176727800 797497608    19% 22154973 99687201   18%   /
    42  
    43  // FetchMetrics interface for mackerelplugin
    44  func (p InodePlugin) FetchMetrics() (map[string]interface{}, error) {
    45  	dfOpt := "-i"
    46  	if runtime.GOOS == "linux" {
    47  		dfOpt = "-iP"
    48  	}
    49  	cmd := exec.Command("df", dfOpt)
    50  	cmd.Env = append(os.Environ(), "LANG=C")
    51  	out, err := cmd.Output()
    52  	if err != nil {
    53  		logger.Warningf("'df -i' command exited with a non-zero status: '%s'", err)
    54  		return nil, err
    55  	}
    56  	result := make(map[string]interface{})
    57  	for _, line := range strings.Split(string(out), "\n") {
    58  		if dfHeaderPattern.MatchString(line) {
    59  			continue
    60  		} else if matches := dfColumnsPattern.FindStringSubmatch(line); matches != nil {
    61  			name := matches[1]
    62  			// https://github.com/docker/docker/blob/v1.5.0/daemon/graphdriver/devmapper/deviceset.go#L981
    63  			if regexp.MustCompile(`^/dev/mapper/docker-`).FindStringSubmatch(name) != nil {
    64  				continue
    65  			}
    66  			if nameMatches := devicePattern.FindStringSubmatch(name); nameMatches != nil {
    67  				device := deviceUnacceptablePattern.ReplaceAllString(nameMatches[1], "_")
    68  				iused, err := strconv.ParseInt(matches[2], 0, 64)
    69  				if err != nil {
    70  					logger.Warningf("Failed to parse value: [%s]", matches[2])
    71  					continue
    72  				}
    73  				ifree, err := strconv.ParseInt(matches[3], 0, 64)
    74  				if err != nil {
    75  					logger.Warningf("Failed to parse value: [%s]", matches[3])
    76  					continue
    77  				}
    78  				result["inode.count."+device+".used"] = uint64(iused)
    79  				result["inode.count."+device+".free"] = uint64(ifree)
    80  				result["inode.count."+device+".total"] = uint64(iused + ifree)
    81  				usedPercentage := 100.0 // 100% if both iused and ifree are 0
    82  				if iused+ifree > 0 {
    83  					usedPercentage = float64(iused) * 100 / float64(iused+ifree)
    84  				}
    85  				result["inode.percentage."+device+".used"] = usedPercentage
    86  			}
    87  		}
    88  	}
    89  	return result, nil
    90  }
    91  
    92  // GraphDefinition interface for mackerelplugin
    93  func (p InodePlugin) GraphDefinition() map[string]mp.Graphs {
    94  	return map[string]mp.Graphs{
    95  		"inode.count.#": {
    96  			Label: "Inode",
    97  			Unit:  "integer",
    98  			Metrics: []mp.Metrics{
    99  				{Name: "used", Label: "used"},
   100  				{Name: "free", Label: "free"},
   101  				{Name: "total", Label: "total"},
   102  			},
   103  		},
   104  		"inode.percentage.#": {
   105  			Label: "Inode Percentage",
   106  			Unit:  "percentage",
   107  			Metrics: []mp.Metrics{
   108  				{Name: "used", Label: "used %"},
   109  			},
   110  		},
   111  	}
   112  }
   113  
   114  // Do the plugin
   115  func Do() {
   116  	inode := InodePlugin{}
   117  	helper := mp.NewMackerelPlugin(inode)
   118  	helper.Run()
   119  }