#!/usr/bin/python3 """ =head1 NAME kvm_net - Munin plugin to show the network I/O per VM =head1 CONFIGURATION parsed environment variables: * vmsuffix: part of vm name to be removed =head1 AUTHOR Copyright (C) 2012 - Igor Borodikhin Copyright (C) 2018 - Lars Kruse =head1 LICENSE GPLv3 =head1 MAGIC MARKERS #%# capabilities=autoconf #%# family=contrib =cut """ import os import re from subprocess import Popen, PIPE import sys def config(vm_names): """ 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 Virtualization graph_info This graph shows the network I/O of the virtual machines graph_args --base 1024 """ print(base_config) for vm in vm_names: print("%s_in.label %s" % (vm, vm)) print("%s_in.type COUNTER" % vm) print("%s_in.min 0" % vm) print("%s_in.draw LINE2" % vm) print("%s_out.negative %s_in" % (vm, vm)) print("%s_out.label %s" % (vm, vm)) print("%s_out.type COUNTER" % vm) print("%s_out.min 0" % vm) print("%s_out.draw LINE2" % vm) 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} """ for pid in vms: tap = get_vm_mac(pid) try: f = open("/proc/net/dev", "r") for line in f.readlines(): if tap in line: print("%s_in.value %s" % (vms[pid], line.split()[1])) print("%s_out.value %s" % (vms[pid], line.split()[9])) break except Exception as inst: print(inst) continue def detect_kvm(): """ Check if kvm is installed """ kvm = Popen(["which", "kvm"], stdout=PIPE) kvm.communicate() return kvm.returncode == 0 def find_vm_names(pids): """Find and clean vm names from pids @return a dictionnary of {pids : cleaned vm name} """ vm_name_regex = re.compile(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$") result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") result[pid] = clean_vm_name(vm_name_regex.sub(r"\1", cmdline.readline())) 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 list_pids(): """ Find the pid of kvm processes @return a list of pids from running kvm """ pid = Popen(["pidof", "qemu-kvm", "qemu-system-x86_64", "kvm"], stdout=PIPE) return pid.communicate()[0].decode().split() if __name__ == "__main__": action = sys.argv[1] if len(sys.argv) > 1 else None if action == "autoconf": if detect_kvm(): print("yes") else: print("no") elif action == "config": vm_data = find_vm_names(list_pids()) config(vm_data.values()) else: vm_data = find_vm_names(list_pids()) fetch(vm_data)