12th October 2014

KVM network restart

While working with KVM virtual networks, it arises often enough the need to edit the network definition, like editing DHCP assignments. This causes all the associated guests to lose network connectivity, and it is required to detach / re-attach their network interfaces. There is a shell script to perform this operation on the default network, with a few constraints.

The documented constraints relates to modifying only the default network, and to assume all guests having just one network interface. To avoid those constraints and allow the attachment of any network, I wrote the following python script, that requires as arguments the network or networks to reattach:

#!/usr/bin/env python
#
# Script to enable restarting a network: 
#       network interfaces are detached / reattached
#
# (c) Luis M Pena  2014
#     Version 1.1
#
# This work is herewith placed in public domain.


import argparse, libvirt, sys, time, subprocess
import xml.etree.ElementTree as XTree

clParser = argparse.ArgumentParser(description='VIRT network restarter')
clParser.add_argument('networks', type=str, nargs='+')
clParser.add_argument('-c', '--connect')
clParser.add_argument('-n', '--no-iptables', action='store_true')
args = clParser.parse_args()


conn = libvirt.open(args.connect)
if not conn:
    hypervisor = args.connect or '(default)'
    print 'Failed to open connection to the hypervisor:', hypervisor
    sys.exit(1)

devices=[]
for network in args.networks:
    try:
        net = conn.networkLookupByName(network)
    except:
        print >> sys.stderr, 'Invalid network name:', network
        continue
    iptables = None if args.no_iptables else subprocess.check_output(['iptables-save'])
    print 'Stopping network', network
    net.destroy()
    for did in conn.listDomainsID():
        domain  = conn.lookupByID(did)
        xmlDesc = domain.XMLDesc(0)
        for n in XTree.fromstring(xmlDesc).findall('devices/interface'):
            if n.get('type')=='network':
                source, mac = [n.find(x) for x in ['source', 'mac']]
                source      = source is not None and source.get('network')
                mac         = mac    is not None and mac.get('address')
                if network == source:
                    message = '%s: detaching device %s, on network %s'
                    print message % (domain.name(), mac, network)
                    xml = XTree.tostring(n)
                    domain.detachDevice(xml)
                    devices.append((domain, mac, xml))
    print 'Starting network', network
    net.create()

if devices:
    print 'Reattaching devices...'
    time.sleep(1)
    for domain, mac, xml in devices:
        print 'Attaching device', mac,'to domain', domain.name()
        domain.attachDevice(xml)
    if iptables:
        print 'Restoring now iptables....'
        p = subprocess.Popen(['iptables-restore'], 
            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = p.communicate(iptables)
        if p.returncode:
            print stdout
            print >> sys.stderr, stderr

Note that there will likely be networking issues after restarting the network, due to the way KVM handles the iptable rules. The best way to avoid these issues is to save / restore the iptables around the script call, which is what this script does by default. To avoid the iptables call, use the argument -n.