summaryrefslogtreecommitdiff
path: root/files/plugins/kvm_net
blob: c7b5acabe3d91053dcd114a98a41e0214dc279b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/python
# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8
#
# Munin plugin to show the network I/O per vm
# On redhat based systems
#
# Copyright Igor Borodikhin
# Copyright Peter Meier
#
# License : GPLv3
#
#
# parsed environment variables:
# vmsuffix: part of vm name to be removed
#
#%# capabilities=autoconf
#%# family=contrib

import re, os, sys
from subprocess import Popen, PIPE

def config(vms):
    ''' Print the plugin's config
    @param vm_names : a list of "cleaned" vms' name
    '''
    base_config = """graph_title KVM Network I/O
graph_vlabel Bytes rx(-)/tx(+) per second
graph_category KVM
graph_info This graph shows the network I/O of the virtual machines
graph_args --base 1024"""
    print base_config
    for pid in vms:
        macs = get_vm_macs(pid)
        i = 0
        for mac in macs:
          print "%s_eth%s_in.label %s_eth%s" % (vms[pid],i, vms[pid], i)
          print "%s_eth%s_in.type COUNTER" % (vms[pid], i)
          print "%s_eth%s_in.min 0" % (vms[pid],i)
          print "%s_eth%s_in.draw LINE2" % (vms[pid],i)
          print "%s_eth%s_out.negative %s_eth%s_in" % (vms[pid], i, vms[pid], i)
          print "%s_eth%s_out.label %s_eth%s" % (vms[pid], i, vms[pid], i)
          print "%s_eth%s_out.type COUNTER" % (vms[pid], i)
          print "%s_eth%s_out.min 0" % (vms[pid], i)
          print "%s_eth%s_out.draw LINE2" % (vms[pid], i)
          i += 1

def clean_vm_name(vm_name):
    ''' Replace all special chars
    @param vm_name : a vm's name
    @return cleaned vm's name
    '''
    # suffix part defined in conf
    suffix = os.getenv('vmsuffix')
    if suffix:
        vm_name = re.sub(suffix,'',vm_name)

    return re.sub(r"[^a-zA-Z0-9_]", "_", vm_name)
    
def fetch(vms):
    ''' Fetch values for a list of pids
    @param dictionnary {kvm_pid: cleaned vm name}
    '''
    res = {}
    macs_to_inf = find_macs_to_inf()
    interfaces = {}
    for e in Popen('cat /proc/net/dev | awk \'{ print $1  ":" $9 }\'', shell=True, stdout=PIPE).communicate()[0].split('\n'):
        s = e.split(':')
        if len(s) == 3:
            interfaces[s[0]] = (s[1],s[2])
    for pid in vms:
        macs = get_vm_macs(pid)
        i = 0
        for mac in macs:
            inf = macs_to_inf[mac]
            values = interfaces[inf]
            if len(values) == 2:
                print "%s_eth%s_in.value %s" % (vms[pid], i, values[0])
                print "%s_eth%s_out.value %s" % (vms[pid], i, values[1])
            i += 1

def detect_kvm():
    ''' Check if kvm is installed
    '''
    kvm = Popen("which kvm", shell=True, stdout=PIPE)
    kvm.communicate()
    return not bool(kvm.returncode)

def find_vm_names(pids):
    '''Find and clean vm names from pids
    @return a dictionnary of {pids : cleaned vm name}
    '''
    result = {}
    for pid in pids:
        cmdline = open("/proc/%s/cmdline" % pid, "r")
        result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-]*)\x00\-.*$",r"\1", cmdline.readline()))        
    return result
    
def get_vm_macs(pid):
    '''Find macs for a pid
    @return the mac addresses for a specified pid
    '''
    cmdline = open("/proc/%s/cmdline" % pid, "r")
    line = cmdline.readline()
    # macs are fe:... on the host
    macs = [ re.sub(r"^\d{2}",'fe',p.split('=')[1]) for p in line.split(",") if re.match(r"^mac(addr)?=",p) ]
    return macs

def list_pids():
    ''' Find the pid of kvm processes
    @return a list of pids from running kvm
    '''
    pid = Popen("pidof qemu-kvm kvm", shell=True, stdout=PIPE)
    return pid.communicate()[0].split()

def find_macs_to_inf():
    ''' Find interfaces for vms
    @return a dictionary of macs to inf
    '''
    result = {}
    inf = ""
    kvm = Popen("ip a | grep -E -A 1 '(tap|vnet)' | awk '{print $2}' | grep -v '^$'", shell=True, stdout=PIPE)
    res = kvm.communicate()[0].split('\n')
    for line in res:
        if len(line) > 0:
            if re.match(r"^tap.*", line):
                inf = re.sub(r"(tap[^:]+):", r"\1", line)
            elif re.match(r"^vnet.*", line):
                inf = re.sub(r"(vnet[^:]+):", r"\1", line)
            else:
                result[line] = inf

    return result
    
if __name__ == "__main__":
    if len(sys.argv) > 1:
        if sys.argv[1] in ['autoconf', 'detect']:
            if detect_kvm():
                print "yes"
            else:
                print "no"
        elif sys.argv[1] == "config":
            config(find_vm_names(list_pids()))
        else:
            fetch(find_vm_names(list_pids()))
    else:
        fetch(find_vm_names(list_pids()))