commit 45a0f99b7e6c3406b160fe7e7969c8661475d1b5
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 22 14:18:29 2013 +0100
VirtUtils: use libvirt bindings instead of virsh
This commit reimplements the communication with libvirt. LNST will not
call virsh anymore, instead libvirt bindings are used for interface
creation and virtual network management.
The main difference is that libvirt bindings use xml strings as
parameters so the classes now use xml templates.
I didn't remove the BridgeCtl class and the shell calls, we might want
to use them in future for something.
I did however remove several useless imports.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/VirtUtils.py | 215 ++++++++++++++++------------------
lnst/Controller/Machine.py | 12 +-
lnst/Controller/NetTestController.py | 15 ++-
3 files changed, 116 insertions(+), 126 deletions(-)
---
diff --git a/lnst/Common/VirtUtils.py b/lnst/Common/VirtUtils.py
index 457e9a0..9390714 100644
--- a/lnst/Common/VirtUtils.py
+++ b/lnst/Common/VirtUtils.py
@@ -11,16 +11,17 @@ __author__ = """
rpazdera(a)redhat.com (Radek Pazdera)
"""
-import os
-import re
-import copy
-import time
import logging
-from tempfile import NamedTemporaryFile
-from xml.dom import minidom
+import libvirt
+from libvirt import libvirtError
from lnst.Common.ExecCmd import exec_cmd, ExecCmdFail
from lnst.Common.NetUtils import normalize_hwaddr, scan_netdevs
+#this is a global object because opening the connection to libvirt in every
+#object instance that uses it sometimes fails - the libvirt server probably
+#can't handle that many connections at a time
+_libvirt_conn = libvirt.open()
+
class VirtUtilsError(Exception):
pass
@@ -54,36 +55,71 @@ def _virsh(cmd):
except ExecCmdFail as err:
raise VirtUtilsError("virsh error: %s" % err)
-# TODO: This class should use the python bindings to libvirt,
-# not the virsh CLI interface
class VirtDomainCtl:
_name = None
+ _net_device_template = """
+ <interface type='network'>
+ <mac address='{0}'/>
+ <source network='{1}'/>
+ <model type='{2}'/>
+ </interface>
+ """
+ _net_device_bare_template = """
+ <interface>
+ <mac address='{0}'/>
+ </interface>
+ """
def __init__(self, domain_name):
self._name = domain_name
+ self._created_interfaces = {}
+
+ try:
+ self._domain = _libvirt_conn.lookupByName(domain_name)
+ except:
+ raise VirtUtilsError("Domain '%s' doesn't exist!" %
domain_name)
def start(self):
- _virsh("start %s" % self._name)
+ self._domain.create()
def stop(self):
- _virsh("destroy %s" % self._name)
+ self._domain.destroy()
def restart(self):
- _virsh("reboot %s" % self._name)
-
- def attach_interface(self, hwaddr, net_name, net_type="bridge"):
- _virsh("attach-interface %s %s %s --mac %s" % \
- (self._name, net_type, net_name, hwaddr))
- self.ifup(hwaddr)
+ self._domain.reboot()
- def detach_interface(self, hwaddr, net_type="bridge"):
- _virsh("detach-interface %s %s %s" % (self._name, net_type, hwaddr))
-
- def ifup(self, hwaddr):
- _virsh("domif-setlink %s %s up" % (self._name, hwaddr))
+ def attach_interface(self, hw_addr, net_name, driver="rtl8139"):
+ try:
+ device_xml = self._net_device_template.format(hw_addr,
+ net_name,
+ driver)
+ self._domain.attachDevice(device_xml)
+ logging.debug("libvirt device with hwaddr '%s' attached" %
hw_addr)
+ self._created_interfaces[hw_addr] = device_xml
+ return True
+ except libvirtError as e:
+ raise VirtUtilsError(str(e))
+
+ def detach_interface(self, hw_addr):
+ if hw_addr in self._created_interfaces:
+ device_xml = self._created_interfaces[hw_addr]
+ else:
+ device_xml = self._net_device_bare_template.format(hw_addr)
- def ifdown(self, hwaddr):
- _virsh("domif-setlink %s %s down" % (self._name, hwaddr))
+ try:
+ self._domain.detachDevice(device_xml)
+ logging.debug("libvirt device with hwaddr '%s' detached" %
hw_addr)
+ return True
+ except libvirtError as e:
+ raise VirtUtilsError(str(e))
+
+ @classmethod
+ def domain_exist(cls, domain_name):
+ try:
+ _libvirt_conn.lookupByName(domain_name)
+ return True
+ except:
+ return False
class NetCtl(object):
_name = None
@@ -101,105 +137,54 @@ class NetCtl(object):
pass
class VirtNetCtl(NetCtl):
- _addr = None
- _mask = None
-
- _dhcp_from = None
- _dhcp_to = None
- _static_mappings = []
- _lease_file = None
-
- def setup_dhcp_server(self, addr, mask, range_start, range_end,
- static_map=None):
- self._addr = addr
- self._mask = mask
- self._dhcp_from = range_start
- self._dhcp_to = range_end
- self._lease_file = "/var/lib/libvirt/dnsmasq/%s.leases" % self._name
- if static_map:
- self._static_mappings = static_map
-
- def get_dhcp_mapping(self, hwaddr, timeout=30):
- wait = timeout
- while True:
- addr = self._get_map(hwaddr)
- if addr or not wait:
- break
-
- time.sleep(1)
- wait -= 1
-
- return addr
+ _network_template = """
+ <network ipv6='yes'>
+ <name>{0}</name>
+ <bridge name='virbr_{0}' stp='off' delay='0' />
+ <domain name='{0}'/>
+ </network>
+ """
- def _get_map(self, hwaddr):
-
- try:
- leases_file = open(self._lease_file, "r")
- except IOError as err:
- raise VirtUtilsError("Unable to resolve IP mapping (%s)" % err)
-
- addr = None
- normalized_hwaddr = normalize_hwaddr(hwaddr)
- for entry in leases_file:
- match = re.match(r"\d+\s+([0-9a-f:]+)\s+([0-9\.]+)", entry)
- if match:
- entry_hwaddr = normalize_hwaddr(match.group(1))
- entry_addr = match.group(2)
- if entry_hwaddr == normalized_hwaddr:
- addr = entry_addr
- break
+ def __init__(self, name=None):
+ if not name:
+ name = self._generate_name()
+ self._name = name
- leases_file.close()
- return addr
+ def _generate_name(self):
+ devs = _libvirt_conn.listNetworks()
+ index = 0
+ while True:
+ name = "lnst_net%d" % index
+ index += 1
+ if name not in devs:
+ return name
def init(self):
- tmp_file = NamedTemporaryFile(delete=False)
- doc = self._get_net_xml_dom()
-
- doc.writexml(tmp_file)
- tmp_file.close()
-
- _virsh("net-create %s" % tmp_file.name)
- os.unlink(tmp_file.name)
+ try:
+ network_xml = self._network_template.format(self._name)
+ _libvirt_conn.networkCreateXML(network_xml)
+ logging.debug("libvirt network '%s' created" % self._name)
+ return True
+ except libvirtError as e:
+ raise VirtUtilsError(str(e))
def cleanup(self):
- _virsh("net-destroy %s" % self._name)
- exec_cmd("rm -f %s" % self._lease_file)
-
- def _get_net_xml_dom(self):
- doc = minidom.Document()
-
- net = doc.createElement("network")
- doc.appendChild(net)
-
- name = doc.createElement("name")
- name_text = doc.createTextNode(self._name)
- name.appendChild(name_text)
- net.appendChild(name)
-
- if self._addr:
- ip = doc.createElement("ip")
- ip.setAttribute("address", self._addr)
- ip.setAttribute("netmask", self._mask)
- net.appendChild(ip)
-
- dhcp = doc.createElement("dhcp")
- ip.appendChild(dhcp)
-
- dhcp_range = doc.createElement("range")
- dhcp_range.setAttribute("start", self._dhcp_from)
- dhcp_range.setAttribute("end", self._dhcp_to)
- dhcp.appendChild(dhcp_range)
-
- for mapping in self._static_mappings:
- hwaddr, addr = mapping
- host = doc.createElement("host")
- host.setAttribute("mac", hwaddr)
- host.setAttribute("ip", addr)
- dhcp.appendChild(host)
-
- return doc
+ try:
+ network = _libvirt_conn.networkLookupByName(self._name)
+ network.destroy()
+ logging.debug("libvirt network '%s' destroyed" %
self._name)
+ return True
+ except libvirtError as e:
+ raise VirtUtilsError(str(e))
+
+ @classmethod
+ def network_exist(cls, net_name):
+ try:
+ _libvirt_conn.networkLookupByName(net_name)
+ return True
+ except:
+ return False
class BridgeCtl(NetCtl):
_remove = False
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 52591fd..8ff372f 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -24,7 +24,7 @@ from pprint import pprint, pformat
from lnst.Common.Config import lnst_config
from lnst.Common.Logs import log_exc_traceback
from lnst.Common.NetUtils import MacPool, normalize_hwaddr
-from lnst.Common.VirtUtils import VirtNetCtl, VirtDomainCtl, BridgeCtl
+from lnst.Common.VirtUtils import VirtNetCtl, VirtDomainCtl
from lnst.Common.Utils import wait_for, md5sum, dir_md5sum, create_tar_archive
from lnst.Common.ConnectionHandler import send_data, recv_data
from lnst.Common.ConnectionHandler import ConnectionHandler
@@ -526,18 +526,18 @@ class VirtualInterface(Interface):
bridges = self._machine.get_network_bridges()
if self._network in bridges:
- brctl = bridges[self._network]
+ net_ctl = bridges[self._network]
else:
- bridges[self._network] = brctl = BridgeCtl()
+ bridges[self._network] = net_ctl = VirtNetCtl()
+ net_ctl.init()
- br_name = brctl.get_name()
- brctl.init()
+ net_name = net_ctl.get_name()
logging.info("Creating interface %s (%s) on machine %s",
self.get_id(), self._hwaddr, self._machine.get_id())
self._orig_hwaddr = self._hwaddr
- domain_ctl.attach_interface(self._hwaddr, br_name)
+ domain_ctl.attach_interface(self._hwaddr, net_name)
# The sleep here is necessary, because udev sometimes renames the
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index cce1678..d1ef7ea 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -21,7 +21,7 @@ import imp
from time import sleep
from xmlrpclib import Binary
from lnst.Common.NetUtils import MacPool
-from lnst.Common.VirtUtils import VirtNetCtl, VirtDomainCtl, BridgeCtl
+from lnst.Common.VirtUtils import VirtNetCtl, VirtDomainCtl
from lnst.Common.Utils import wait_for, md5sum, dir_md5sum, create_tar_archive
from lnst.Common.Utils import check_process_running, bool_it
from lnst.Common.NetTestCommand import NetTestCommandContext, NetTestCommand
@@ -467,13 +467,18 @@ class NetTestController:
domain_ctl = VirtDomainCtl(libvirt_dom)
logging.info("Detaching dynamically created interfaces.")
for i in machine["interfaces"]:
- domain_ctl.detach_interface(i)
+ try:
+ domain_ctl.detach_interface(i)
+ except:
+ pass
logging.info("Removing dynamically created bridges.")
for br in cfg["bridges"]:
- br_ctl = BridgeCtl(br)
- br_ctl.set_remove(True)
- br_ctl.cleanup()
+ try:
+ net_ctl = VirtNetCtl(br)
+ net_ctl.cleanup()
+ except:
+ pass
os.remove("/tmp/.lnst_virt_conf")