Révision e0df6aa7
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.
| plugins/libvirt/kvm_net | ||
|---|---|---|
| 6 | 6 |
kvm_net - Munin plugin to show the network I/O per VM |
| 7 | 7 |
|
| 8 | 8 |
|
| 9 |
=head1 APPLICABLE SYSTEMS |
|
| 10 |
|
|
| 11 |
Virtualization server with VMs based on KVM may be able to track the network |
|
| 12 |
traffic of their VMs, if the KVM processes are started in a specific way. |
|
| 13 |
|
|
| 14 |
Probably proxmox-based virtualization hosts fit into this category. |
|
| 15 |
|
|
| 16 |
You can easily check if your KVM processes are started in the expected way, by |
|
| 17 |
running the following command: |
|
| 18 |
|
|
| 19 |
ps -ef | grep "netdev.*ifname=" |
|
| 20 |
|
|
| 21 |
The plugin can be used, if the above command outputs one line for every |
|
| 22 |
currently running VM. |
|
| 23 |
|
|
| 24 |
In all other cases you need to use other munin plugins instead, e.g. "libvirt". |
|
| 25 |
|
|
| 26 |
|
|
| 9 | 27 |
=head1 CONFIGURATION |
| 10 | 28 |
|
| 11 | 29 |
parsed environment variables: |
| ... | ... | |
| 39 | 57 |
|
| 40 | 58 |
|
| 41 | 59 |
VM_NAME_REGEX = re.compile("^.*\x00-{arg_name}\x00(.+)\x00.*$")
|
| 60 |
KVM_INTERFACE_NAME_REGEX = re.compile("(?:^|,)ifname=([^,]+)(?:,|$)")
|
|
| 42 | 61 |
|
| 43 | 62 |
|
| 44 | 63 |
def config(vm_names): |
| ... | ... | |
| 83 | 102 |
|
| 84 | 103 |
@param dictionnary {kvm_pid: cleaned vm name}
|
| 85 | 104 |
""" |
| 86 |
for pid in vms: |
|
| 87 |
tap = get_vm_mac(pid) |
|
| 88 |
try: |
|
| 89 |
f = open("/proc/net/dev", "r")
|
|
| 90 |
for line in f.readlines(): |
|
| 91 |
if tap in line: |
|
| 92 |
print("%s_in.value %s" % (vms[pid], line.split()[1]))
|
|
| 93 |
print("%s_out.value %s" % (vms[pid], line.split()[9]))
|
|
| 94 |
break |
|
| 95 |
except Exception as inst: |
|
| 96 |
print(inst) |
|
| 97 |
continue |
|
| 105 |
for pid, vm_data in vms.items(): |
|
| 106 |
vm_interface_names = get_vm_network_interface_names(pid) |
|
| 107 |
sum_incoming = 0 |
|
| 108 |
sum_outgoing = 0 |
|
| 109 |
interface_found = False |
|
| 110 |
with open("/proc/net/dev", "r") as net_file:
|
|
| 111 |
for line in net_file.readlines(): |
|
| 112 |
tokens = line.split() |
|
| 113 |
current_interface_name = tokens[0].rstrip(":").strip()
|
|
| 114 |
if current_interface_name in vm_interface_names: |
|
| 115 |
sum_incoming += int(tokens[1]) |
|
| 116 |
sum_outgoing += int(tokens[9]) |
|
| 117 |
interface_found = True |
|
| 118 |
if not interface_found: |
|
| 119 |
# we want to distinguish "no traffic" from "not found" |
|
| 120 |
sum_incoming = "U" |
|
| 121 |
sum_outgoing = "U" |
|
| 122 |
print("%s_in.value %s" % (vm_data, sum_incoming))
|
|
| 123 |
print("%s_out.value %s" % (vm_data, sum_outgoing))
|
|
| 124 |
|
|
| 125 |
|
|
| 126 |
def get_vm_network_interface_names(pid): |
|
| 127 |
""" return the MAC addresses configured for network interfacs of a PID """ |
|
| 128 |
result = set() |
|
| 129 |
for netdev_description in _get_kvm_process_arguments(pid, "netdev"): |
|
| 130 |
match = KVM_INTERFACE_NAME_REGEX.search(netdev_description) |
|
| 131 |
if match: |
|
| 132 |
result.add(match.groups()[0]) |
|
| 133 |
return result |
|
| 98 | 134 |
|
| 99 | 135 |
|
| 100 | 136 |
def detect_kvm(): |
| ... | ... | |
| 139 | 175 |
return result |
| 140 | 176 |
|
| 141 | 177 |
|
| 142 |
def get_vm_mac(pid): |
|
| 143 |
"""Find and clean vm names from pids |
|
| 144 |
|
|
| 145 |
@return the mac address for a specified pid |
|
| 146 |
""" |
|
| 147 |
cmdline = open("/proc/%s/cmdline" % pid, "r")
|
|
| 148 |
line = cmdline.readline() |
|
| 149 |
mac = re.sub(r"^.*ifname=(tap[^,]+),.*$", r"\1", line) |
|
| 150 |
return mac |
|
| 151 |
|
|
| 152 |
|
|
| 153 | 178 |
def _get_kvm_process_arguments(pid, arg_name): |
| 154 | 179 |
""" parse all value with the given name from the process identified by PID |
| 155 | 180 |
|
Formats disponibles : Unified diff