Sunday, March 17, 2013

Libvirt: Metering Domain Network Traffic

We can use the libvirt API to get network traffic at the interface level. So to meter the network traffic at the domain level, we have to find all interfaces the domain is using, and aggregate the traffic stats. This isn't too difficult — we just have to query the running domains, and query their XML descriptions individually in order to find the interfaces we need. One challenge we might face, using this approach, is the fact that any two interfaces could be using different networks. For example, let's say that we only want network traffic on the default network? Here is an example of how to generate this network traffic in Python. We're using the collections and the etree modules.

from xml.etree import ElementTree
from collections import Counter
import libvirt

def get_interfaces(domain, network=None):

    tree = ElementTree.fromstring(domain.XMLDesc(1))
    path = './devices/interface'

    if network is not None:
        path += '/source[@network="%s"]/..' % network
    
    return [
        i.find('target').get('dev')
        for i in tree.findall(path)
    ]

def meter_traffic(domain, network=None):

    meter = Counter(rx_bytes=0, tx_bytes=0)

    for i in get_interfaces(domain, network):

        stats = domain.interfaceStats(i)

        meter.update(dict(
            rx_bytes = stats[0],
            tx_bytes = stats[4]
        ))

    return meter

if __name__ == '__main__':

    conn = libvirt.open('qemu:///system')

    print 'Traffic...\n'

    for d in conn.listDomainsID():

        domain = conn.lookupByID(d)
        traffic = meter_traffic(domain, 'default')
        
        print domain.name()
        print 'Inbound: %d' % traffic.get('rx_bytes')
        print 'Outbound: %d\n' % traffic.get('tx_bytes')