2
0
mirror of https://github.com/munin-monitoring/contrib.git synced 2018-11-08 00:59:34 +01:00

kvm_net: improve the network interface name parser and admit its limits

The network interface parser of this plugin was overly specific before.
It relied on a specific format of the arguments handed over to kvm while
starting the VM.  For example the following format was usable:

  ... -netdev tap,ifname=foo,...

But kvm/qemu support a variety of ways for configuring network
interfaces via the commandline.  E.g. libvirt does not use the "ifname"
parameter above.  Thus VMs running on a host controlled via libvirt
cannot be tracked with this plugin.

This limititation is now clearly documented in the header of the plugin.
This commit is contained in:
Lars Kruse 2018-06-10 21:53:39 +02:00
parent b6c6a02efe
commit e0df6aa788

View File

@ -6,6 +6,24 @@
kvm_net - Munin plugin to show the network I/O per VM kvm_net - Munin plugin to show the network I/O per VM
=head1 APPLICABLE SYSTEMS
Virtualization server with VMs based on KVM may be able to track the network
traffic of their VMs, if the KVM processes are started in a specific way.
Probably proxmox-based virtualization hosts fit into this category.
You can easily check if your KVM processes are started in the expected way, by
running the following command:
ps -ef | grep "netdev.*ifname="
The plugin can be used, if the above command outputs one line for every
currently running VM.
In all other cases you need to use other munin plugins instead, e.g. "libvirt".
=head1 CONFIGURATION =head1 CONFIGURATION
parsed environment variables: parsed environment variables:
@ -39,6 +57,7 @@ import sys
VM_NAME_REGEX = re.compile("^.*\x00-{arg_name}\x00(.+)\x00.*$") VM_NAME_REGEX = re.compile("^.*\x00-{arg_name}\x00(.+)\x00.*$")
KVM_INTERFACE_NAME_REGEX = re.compile("(?:^|,)ifname=([^,]+)(?:,|$)")
def config(vm_names): def config(vm_names):
@ -83,18 +102,35 @@ def fetch(vms):
@param dictionnary {kvm_pid: cleaned vm name} @param dictionnary {kvm_pid: cleaned vm name}
""" """
for pid in vms: for pid, vm_data in vms.items():
tap = get_vm_mac(pid) vm_interface_names = get_vm_network_interface_names(pid)
try: sum_incoming = 0
f = open("/proc/net/dev", "r") sum_outgoing = 0
for line in f.readlines(): interface_found = False
if tap in line: with open("/proc/net/dev", "r") as net_file:
print("%s_in.value %s" % (vms[pid], line.split()[1])) for line in net_file.readlines():
print("%s_out.value %s" % (vms[pid], line.split()[9])) tokens = line.split()
break current_interface_name = tokens[0].rstrip(":").strip()
except Exception as inst: if current_interface_name in vm_interface_names:
print(inst) sum_incoming += int(tokens[1])
continue sum_outgoing += int(tokens[9])
interface_found = True
if not interface_found:
# we want to distinguish "no traffic" from "not found"
sum_incoming = "U"
sum_outgoing = "U"
print("%s_in.value %s" % (vm_data, sum_incoming))
print("%s_out.value %s" % (vm_data, sum_outgoing))
def get_vm_network_interface_names(pid):
""" return the MAC addresses configured for network interfacs of a PID """
result = set()
for netdev_description in _get_kvm_process_arguments(pid, "netdev"):
match = KVM_INTERFACE_NAME_REGEX.search(netdev_description)
if match:
result.add(match.groups()[0])
return result
def detect_kvm(): def detect_kvm():
@ -139,17 +175,6 @@ def find_vm_names(pids):
return result return result
def get_vm_mac(pid):
"""Find and clean vm names from pids
@return the mac address for a specified pid
"""
cmdline = open("/proc/%s/cmdline" % pid, "r")
line = cmdline.readline()
mac = re.sub(r"^.*ifname=(tap[^,]+),.*$", r"\1", line)
return mac
def _get_kvm_process_arguments(pid, arg_name): def _get_kvm_process_arguments(pid, arg_name):
""" parse all value with the given name from the process identified by PID """ parse all value with the given name from the process identified by PID