Created
July 28, 2019 18:00
-
-
Save TobleMiner/d2d715f0651618facd2e612369b092bd to your computer and use it in GitHub Desktop.
Simple python script to reattach host devices to libvirt guests when they are hotplugged on the host
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ACTION=="bind", SUBSYSTEMS=="usb", RUN="/usr/local/sbin/libvirt-hotplug.py usb" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import libvirt | |
import xml.etree.ElementTree as ET | |
import sys | |
import os | |
import re | |
RE_HEX_SUFFIX = re.compile('([0-9a-fA-F]+)$') | |
def hex_suffix(s): | |
match = RE_HEX_SUFFIX.search(s) | |
if match == None: | |
return '' | |
return match.groups()[0] | |
def usage(bin): | |
print("Usage: {} <bus>".format(bin)) | |
if len(sys.argv) < 2: | |
print("Too few arguments") | |
usage(sys.argv[0]) | |
sys.exit(1) | |
bus = sys.argv[1] | |
if bus != 'usb': | |
print('Sorry, atm only usb devices are supported') | |
sys.exit(1) | |
if not 'ID_VENDOR_ID' in os.environ or \ | |
not 'ID_MODEL_ID' in os.environ: | |
sys.exit() | |
vid = os.environ['ID_VENDOR_ID'].lower() | |
pid = os.environ['ID_MODEL_ID'].lower() | |
conn = libvirt.open(None) | |
if not conn: | |
raise Exception("Failed to connect to libvirt") | |
def handle_dev_usb(hostdev): | |
vendor = hostdev.find("./source/vendor") | |
product = hostdev.find("./source/product") | |
if vendor == None or product == None: | |
print("Invalid XML, needs 'vendor' and 'product'") | |
return False | |
if not 'id' in vendor.attrib or not 'id' in product.attrib: | |
return False | |
candidate_vid = hex_suffix(vendor.attrib['id']).lower() | |
candidate_pid = hex_suffix(product.attrib['id']).lower() | |
return candidate_vid == vid and candidate_pid == pid | |
def hostdev_purge_address(dev): | |
source = dev.find('./source') | |
if source != None: | |
address = source.find('./address') | |
if address != None: | |
source.remove(address) | |
# Iterate through active domains | |
for domid in conn.listDomainsID(): | |
dom = conn.lookupByID(domid) | |
xml = ET.fromstring(dom.XMLDesc(domid)) | |
# Retrieve all host devices on the bus | |
hostdevs = xml.findall(".//hostdev[@type='{}']".format(bus)) | |
for dev in hostdevs: | |
if handle_dev_usb(dev): | |
print("Attaching device [{}:{}] to {}".format(vid, pid, dom.name())) | |
devxml = ET.tostring(dev, encoding='unicode') | |
dom.detachDevice(devxml) | |
# Remove address node, libvirt will figure that one out by itself | |
hostdev_purge_address(dev) | |
devxml = ET.tostring(dev, encoding='unicode') | |
dom.attachDevice(devxml) | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment