Skip to content

Commit

Permalink
Update irqstat plugin to allow parsing test
Browse files Browse the repository at this point in the history
  • Loading branch information
calerogers committed Mar 20, 2017
1 parent 24bdd8e commit 8443251
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 77 deletions.
21 changes: 10 additions & 11 deletions plugins/inputs/irqstat/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Irqstat Input Plugin

The irqstat plugin gathers metrics about the interrupt types and associated values from `/proc/interrupts` and `/proc/softirqs` for each CPU present on a system.
The irqstat plugin gathers metrics about IRQs from `/proc/interrupts` and `/proc/softirqs`.

### Configuration
```
Expand All @@ -12,10 +12,10 @@ The above configuration would result in an output similar to:
```
./telegraf -config ~/irqstat_config.conf -test
* Plugin: inputs.irqstat, Collection 1
> interrupts,irq=30,type=PCI-MSI,device=65537-edge\ virtio1-input.0,host=hostname CPU0=1i,total=1i 1489346531000000000
> interrupts,irq=0,type=IO-APIC,device=2-edge\ timer,host=hostname CPU0=23i,total=23i 1489346531000000000
> interrupts,irq=1,host=hostname,type=IO-APIC,device=1-edge\ i8042 CPU0=9i,total=9i 1489346531000000000
> interrupts,irq=30,type=PCI-MSI,device=65537-edge\ virtio1-input.0,host=hostname CPU0=1i,total=1i 1489346531000000000
> soft_interrupts,irq=NET_RX,host=hostname CPU0=280879i,total=280879i 1489346531000000000
> interrupts,irq=0,type=IO-APIC,device=2-edge\ timer,host=hostname CPU0=23i,total=23i 1489346531000000000
```

# Measurements
Expand All @@ -24,11 +24,10 @@ There are two measurements reported by this plugin.
- `interrupts` reports metrics from the `/proc/interrupts` file
- `soft_interrupts` reports metrics from the `/proc/softirqs` file

Depending on the content of each file there will multiple tags and fields for each measurement
- <strong>Fields:</strong>
- CPUx: the IRQ value based on CPU number
- Total: total IRQ value of all CPUs
- <strong>Tags:</strong>
- IRQ: the IRQ
- Type: the type associated with the IRQ
- Device: the device associated with the IRQ
#### Fields:
- CPUx: the amount of interrupts for the IRQ handled by that CPU
- Total: total amount of interrupts for all CPUs
#### Tags:
- IRQ: the interrupt ID
- Type: the type of interrupt
- Device: the name of the device that is located at that IRQ
111 changes: 45 additions & 66 deletions plugins/inputs/irqstat/irqstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,47 @@ import (
"fmt"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
)

type Irqstat struct {
Include []string
Irqmap map[string]map[string]interface{}
}

func NewIrqstat() *Irqstat {
return &Irqstat{
Irqmap: make(map[string]map[string]interface{}),
}
type IRQ struct {
ID string
Fields map[string]interface{}
Tags map[string]string
}

func NewIRQ(id string) *IRQ {
return &IRQ{ID: id, Fields: make(map[string]interface{}), Tags: make(map[string]string)}
}

const sampleConfig = `
## A list of IRQs to include for metric ingestion, if not specified
## will default to collecting all IRQs.
# include = ["0", "1"]
# include = ["0", "1", "30", "NET_RX"]
`

func (s *Irqstat) Description() string {
return "This plugin gathers IRQ types and associated values from /proc/interrupts and /proc/softirqs for each CPU."
return "This plugin gathers interrupts data from /proc/interrupts and /proc/softirqs."
}

func (s *Irqstat) SampleConfig() string {
return sampleConfig
}

func (s *Irqstat) ParseIrqFile(path string) {
func parseInterrupts(irqdata string, include []string) []IRQ {
var irqs []IRQ
var cpucount int
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(file)
scanner := bufio.NewScanner(strings.NewReader(irqdata))
for scanner.Scan() {
var irqval int64
var irqtotal int64
irqdesc := "none"
irqdevice := "none"
var irqval, irqtotal int64
var irqtype, irqdevice string
fields := strings.Fields(scanner.Text())
ff := fields[0]
if ff == "CPU0" {
Expand All @@ -56,40 +54,43 @@ func (s *Irqstat) ParseIrqFile(path string) {

if ff[len(ff)-1:] == ":" {
fields = fields[1:len(fields)]
irqtype := ff[:len(ff)-1]
if path == "/proc/softirqs" {
irqtype = irqtype + "_softirq"
}
_, err := strconv.ParseInt(irqtype, 10, 64)
irqid := ff[:len(ff)-1]
irq := NewIRQ(irqid)
_, err := strconv.ParseInt(irqid, 10, 64)
if err == nil {
irqdesc = fields[cpucount]
irqtype = fields[cpucount]
irqdevice = strings.Join(fields[cpucount+1:], " ")
} else {
if len(fields) > cpucount {
irqdesc = strings.Join(fields[cpucount:], " ")
irqtype = strings.Join(fields[cpucount:], " ")
}
}
for i := 0; i < cpucount; i++ {
cpukey := fmt.Sprintf("CPU%d", i)
if s.Irqmap[irqtype] == nil {
s.Irqmap[irqtype] = make(map[string]interface{})
}
cpu := fmt.Sprintf("CPU%d", i)
irq.Tags["irq"] = irqid
irqval = 0
if i < len(fields) {
irqval, err = strconv.ParseInt(fields[i], 10, 64)
if err != nil {
log.Fatal(err)
}
}
s.Irqmap[irqtype][cpukey] = irqval
irq.Fields[cpu] = irqval
irqtotal = irqval + irqtotal
}
s.Irqmap[irqtype]["type"] = irqdesc
s.Irqmap[irqtype]["device"] = irqdevice
s.Irqmap[irqtype]["total"] = irqtotal
irq.Tags["type"] = irqtype
irq.Tags["device"] = irqdevice
irq.Fields["total"] = irqtotal
if len(include) == 0 {
irqs = append(irqs, *irq)
} else {
if stringInSlice(irq.ID, include) {
irqs = append(irqs, *irq)
}
}
}
}
file.Close()
return irqs
}

func stringInSlice(x string, list []string) bool {
Expand All @@ -102,41 +103,19 @@ func stringInSlice(x string, list []string) bool {
}

func (s *Irqstat) Gather(acc telegraf.Accumulator) error {
irqtags := make(map[string]string)
irqfields := make(map[string]interface{})
files := []string{"/proc/interrupts", "/proc/softirqs"}
for _, file := range files {
s.ParseIrqFile(file)
}
for irq, fields := range s.Irqmap {
irqtype := strings.Split(irq, "_softirq")[0]
irqtags["irq"] = irqtype
for k, v := range fields {
switch t := v.(type) {
case int64:
irqfields[k] = t
case string:
irqtags[k] = t
}
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatal(err)
}
for k, _ := range irqtags {
if irqtags[k] == "none" {
delete(irqtags, k)
}
}
if len(s.Include) == 0 {
if strings.HasSuffix(irq, "_softirq") {
acc.AddFields("soft_interrupts", irqfields, irqtags)
irqdata := string(data)
irqs := parseInterrupts(irqdata, s.Include)
for _, irq := range irqs {
if file == "/proc/softirqs" {
acc.AddFields("soft_interrupts", irq.Fields, irq.Tags)
} else {
acc.AddFields("interrupts", irqfields, irqtags)
}
} else {
if stringInSlice(irqtype, s.Include) {
if strings.HasSuffix(irq, "_softirq") {
acc.AddFields("soft_interrupts", irqfields, irqtags)
} else {
acc.AddFields("interrupts", irqfields, irqtags)
}
acc.AddFields("interrupts", irq.Fields, irq.Tags)
}
}
}
Expand All @@ -145,6 +124,6 @@ func (s *Irqstat) Gather(acc telegraf.Accumulator) error {

func init() {
inputs.Add("irqstat", func() telegraf.Input {
return NewIrqstat()
return &Irqstat{}
})
}
38 changes: 38 additions & 0 deletions plugins/inputs/irqstat/irqstat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package irqstat

import "testing"

func TestParseInterrupts(t *testing.T) {
include := []string{}
interruptStr := ` CPU0 CPU1
0: 134 0 IO-APIC-edge timer
1: 7 3 IO-APIC-edge i8042
NMI: 0 0 Non-maskable interrupts
LOC: 2338608687 2334309625 Local timer interrupts
MIS: 0`

parsed := []IRQ{
IRQ{ID: "0", Fields: map[string]interface{}{"CPU0": int64(134), "CPU1": int64(0)}, Tags: map[string]string{"type": "IO-APIC-edge", "device": "timer"}},
IRQ{ID: "1", Fields: map[string]interface{}{"CPU0": int64(7), "CPU1": int64(3)}, Tags: map[string]string{"type": "IO-APIC-edge", "device": "i8042"}},
IRQ{ID: "NMI", Fields: map[string]interface{}{"CPU0": int64(0), "CPU1": int64(0)}, Tags: map[string]string{"type": "Non-maskable interrupts"}},
IRQ{ID: "LOC", Fields: map[string]interface{}{"CPU0": int64(2338608687), "CPU1": int64(2334309625)}, Tags: map[string]string{"type": "Local timer interrupts"}},
IRQ{ID: "MIS", Fields: map[string]interface{}{"CPU0": int64(0), "CPU1": int64(0)}},
}

got := parseInterrupts(interruptStr, include)
if len(got) == 0 {
t.Fatalf("want %+v, got %+v", parsed, got)
}
for i := 0; i < len(parsed); i++ {
for k, _ := range parsed[i].Fields {
if parsed[i].Fields[k] != got[i].Fields[k] {
t.Fatalf("want %+v, got %+v", parsed[i].Fields[k], got[i].Fields[k])
}
}
for k, _ := range parsed[i].Tags {
if parsed[i].Tags[k] != got[i].Tags[k] {
t.Fatalf("want %+v, got %+v", parsed[i].Tags[k], got[i].Tags[k])
}
}
}
}

0 comments on commit 8443251

Please sign in to comment.