[lnst] Fix missing colon after new function recipe_head_log_entry
by Jiří Pírko
commit 09e7c3cae015943f5f55d6c8a39dbec3b9f53f9e
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Thu Nov 28 17:41:31 2013 +0100
Fix missing colon after new function recipe_head_log_entry
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst-ctl | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/lnst-ctl b/lnst-ctl
index fadeeb8..80b1308 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -106,7 +106,7 @@ def get_recipe_result(args, file_path, res_serializer, packet_capture,
return (file_path, res, retval)
-def recipe_head_log_entry(filename)
+def recipe_head_log_entry(filename):
head_str = "\nProcessing recipe file \"%s\"\n" % filename
logging.info("-" * len(head_str.strip())
+ head_str
9 years, 10 months
[PATCH] Fix missing colon after new function recipe_head_log_entry
by Jan Tluka
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst-ctl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnst-ctl b/lnst-ctl
index fadeeb8..80b1308 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -106,7 +106,7 @@ def get_recipe_result(args, file_path, res_serializer, packet_capture,
return (file_path, res, retval)
-def recipe_head_log_entry(filename)
+def recipe_head_log_entry(filename):
head_str = "\nProcessing recipe file \"%s\"\n" % filename
logging.info("-" * len(head_str.strip())
+ head_str
--
1.8.1.4
9 years, 10 months
[lnst] VirtualInterface: add support for setting device driver
by Jiří Pírko
commit 45aaf962a338ccc13825c0a8d455a428b8ca2495
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 22 14:37:08 2013 +0100
VirtualInterface: add support for setting device driver
Using the libvirt API binding allows us to create dynamic devices with
specific device drivers easily. This commit adds that functionality.
To specify a device driver you need to set a parameter of your
dynamically created device in your recipe like this:
<eth ...>
...
<params>
<param name="driver" value="e1000"/>
</params>
...
</eth>
Keep in mind that this will be used as a matching restriction for
matching of physical devices, and when matching virtual machines the
value needs to be supported by libvirt. An invalid value will result in
an exception being raised.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Machine.py | 9 ++++++++-
lnst/Controller/SlavePool.py | 8 +++++---
2 files changed, 13 insertions(+), 4 deletions(-)
---
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 8ff372f..1c77f38 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -499,6 +499,13 @@ class VirtualInterface(Interface):
"""
def __init__(self, machine, if_id, if_type):
super(VirtualInterface, self).__init__(machine, if_id, if_type)
+ self._driver = "rtl8139"
+
+ def set_driver(self, driver):
+ self._driver = driver
+
+ def get_driver(self):
+ return self._driver
def get_orig_hwaddr(self):
if not self._orig_hwaddr:
@@ -537,7 +544,7 @@ class VirtualInterface(Interface):
self.get_id(), self._hwaddr, self._machine.get_id())
self._orig_hwaddr = self._hwaddr
- domain_ctl.attach_interface(self._hwaddr, net_name)
+ domain_ctl.attach_interface(self._hwaddr, net_name, self._driver)
# The sleep here is necessary, because udev sometimes renames the
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index 7266eaf..00ee4b8 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -307,6 +307,8 @@ class SlavePool:
iface.set_network(if_data["network"])
if "hwaddr" in if_data["params"]:
iface.set_hwaddr(if_data["params"]["hwaddr"])
+ if "driver" in if_data["params"]:
+ iface.set_driver(if_data["params"]["driver"])
return machine
@@ -673,10 +675,10 @@ class SetupMapper:
for if_id, interface in m["interfaces"].iteritems():
if "params" in interface:
for name, val in interface["params"].iteritems():
- if name != "hwaddr":
+ if name not in ["hwaddr", "driver"]:
msg = "Dynamically created interfaces "\
- "only support the 'hwaddr' option. "\
- "'%s=%s' found on machine '%s' "\
+ "only support the 'hwaddr' and 'driver' "\
+ "option. '%s=%s' found on machine '%s' "\
"interface '%s'" % (name, val,
m_id, if_id)
raise MapperError(msg)
9 years, 10 months
[lnst] VirtUtils: use libvirt bindings instead of virsh
by Jiří Pírko
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")
9 years, 10 months
[lnst] NmConfigDevice: add port config to team slaves
by Jiří Pírko
commit 28bff09407287e8c06943d89b5832c282a17461e
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 22 14:16:16 2013 +0100
NmConfigDevice: add port config to team slaves
When I implemented the team configuration through NM I forgot about port
configuration of team slaves. This commit fixes that.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/NmConfigDevice.py | 11 ++++++++++-
1 files changed, 10 insertions(+), 1 deletions(-)
---
diff --git a/lnst/Slave/NmConfigDevice.py b/lnst/Slave/NmConfigDevice.py
index affb0a9..4901729 100644
--- a/lnst/Slave/NmConfigDevice.py
+++ b/lnst/Slave/NmConfigDevice.py
@@ -748,6 +748,11 @@ class NmConfigDeviceTeam(NmConfigDeviceGeneric):
hw_addr = self._convert_hwaddr(netdev)
+ teamd_port_config = get_slave_option(self._netdev,
+ slave, "teamd_port_config")
+ if teamd_port_config == None:
+ teamd_port_config = ""
+
s_eth = dbus.Dictionary({
'duplex': dbus.Array('full', 's'),
'mac-address': hw_addr})
@@ -760,9 +765,13 @@ class NmConfigDeviceTeam(NmConfigDeviceGeneric):
'master': self._netdev["master_uuid"],
'slave-type': 'team'})
+ s_port_cfg = dbus.Dictionary({
+ 'config': teamd_port_config})
+
slave_con = dbus.Dictionary({
'802-3-ethernet': s_eth,
- 'connection': s_slave_con})
+ 'connection': s_slave_con,
+ 'team-port': s_port_cfg})
netdev["con_obj_path"] = self._nm_add_connection(slave_con)
9 years, 10 months
[lnst] SlavePool: virtual machine creation ignored custom rpcport
by Jiří Pírko
commit 6f8ca5f4221a7235a1d0b305525e194503e26fb3
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Fri Nov 22 14:15:50 2013 +0100
SlavePool: virtual machine creation ignored custom rpcport
Setting the rpc_port parameter in a slave pool had no effect for virtual
machines, which resulted in the machine being recognized as available
but the controller could'nt connect to it.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/SlavePool.py | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
---
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index e7d4557..7266eaf 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -289,7 +289,11 @@ class SlavePool:
hostname = pm["params"]["hostname"]
libvirt_domain = pm["params"]["libvirt_domain"]
- machine = Machine(tm_id, hostname, libvirt_domain)
+ rpcport = None
+ if "rpc_port" in pm["params"]:
+ rpcport = pm["params"]["rpc_port"]
+
+ machine = Machine(tm_id, hostname, libvirt_domain, rpcport)
# make all the existing unused
for if_id, if_data in pm["interfaces"].iteritems():
9 years, 10 months
[lnst] lnst-ctl: enhance readability of info about processed recipes
by Jiří Pírko
commit f9d80651d757ad48ddda16fb96f89237f82f7b3d
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Wed Nov 20 16:01:28 2013 +0100
lnst-ctl: enhance readability of info about processed recipes
Resending version 2 of the patch. I moved the code for generating log
entry into a function to avoid code duplication.
This is a patch that would slightly enhance readability of controller
log.
First change is addition of a missing log entry when recipes are passed
as
individual files.
Second change is modification of the log entry so it's obvious that
another recipe's being processed.
Before,
Loading config file '/root/lnst/lnst-ctl.conf'
Loading config file '/root/.lnst/lnst-ctl.conf'
2013-11-20 09:12:17 (localhost) INFO: Processing recipe file "recipes/team/recipex_ab_001.xml"
2013-11-20 09:12:17 (localhost) INFO: Checking machine pool availability.
...
After,
Loading config file '/root/lnst/lnst-ctl.conf'
Loading config file '/root/.lnst/lnst-ctl.conf'
2013-11-20 09:14:43 (localhost) INFO:
--------------------------------------------------------
Processing recipe file "recipes/team/recipex_ab_001.xml"
--------------------------------------------------------
2013-11-20 09:14:43 (localhost) INFO: Checking machine pool availability.
...
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst-ctl | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
---
diff --git a/lnst-ctl b/lnst-ctl
index d597c23..fadeeb8 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -106,6 +106,12 @@ def get_recipe_result(args, file_path, res_serializer, packet_capture,
return (file_path, res, retval)
+def recipe_head_log_entry(filename)
+ head_str = "\nProcessing recipe file \"%s\"\n" % filename
+ logging.info("-" * len(head_str.strip())
+ + head_str
+ + "-" * len(head_str.strip()))
+
def main():
"""
Main function
@@ -213,7 +219,7 @@ def main():
for f in all_files:
recipe_file = os.path.join(recipe_path, f)
if re.match(r'^.*\.xml$', recipe_file):
- logging.info("Processing recipe file \"%s\"" % recipe_file)
+ recipe_head_log_entry(recipe_file)
fp, res, rv = get_recipe_result(action, recipe_file,
res_serializer,
packet_capture,
@@ -221,6 +227,7 @@ def main():
defined_aliases,
overriden_aliases)
else:
+ recipe_head_log_entry(recipe_path)
fp, res, rv = get_recipe_result(action, recipe_path,
res_serializer,
packet_capture,
9 years, 10 months
[PATCH] VirtualInterface: add support for setting device driver
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
Using the libvirt API binding allows us to create dynamic devices with
specific device drivers easily. This commit adds that functionality.
To specify a device driver you need to set a parameter of your
dynamically created device in your recipe like this:
<eth ...>
...
<params>
<param name="driver" value="e1000"/>
</params>
...
</eth>
Keep in mind that this will be used as a matching restriction for
matching of physical devices, and when matching virtual machines the
value needs to be supported by libvirt. An invalid value will result in
an exception being raised.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/Machine.py | 9 ++++++++-
lnst/Controller/SlavePool.py | 8 +++++---
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 8ff372f..1c77f38 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -499,6 +499,13 @@ class VirtualInterface(Interface):
"""
def __init__(self, machine, if_id, if_type):
super(VirtualInterface, self).__init__(machine, if_id, if_type)
+ self._driver = "rtl8139"
+
+ def set_driver(self, driver):
+ self._driver = driver
+
+ def get_driver(self):
+ return self._driver
def get_orig_hwaddr(self):
if not self._orig_hwaddr:
@@ -537,7 +544,7 @@ class VirtualInterface(Interface):
self.get_id(), self._hwaddr, self._machine.get_id())
self._orig_hwaddr = self._hwaddr
- domain_ctl.attach_interface(self._hwaddr, net_name)
+ domain_ctl.attach_interface(self._hwaddr, net_name, self._driver)
# The sleep here is necessary, because udev sometimes renames the
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index 7266eaf..00ee4b8 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -307,6 +307,8 @@ class SlavePool:
iface.set_network(if_data["network"])
if "hwaddr" in if_data["params"]:
iface.set_hwaddr(if_data["params"]["hwaddr"])
+ if "driver" in if_data["params"]:
+ iface.set_driver(if_data["params"]["driver"])
return machine
@@ -673,10 +675,10 @@ class SetupMapper:
for if_id, interface in m["interfaces"].iteritems():
if "params" in interface:
for name, val in interface["params"].iteritems():
- if name != "hwaddr":
+ if name not in ["hwaddr", "driver"]:
msg = "Dynamically created interfaces "\
- "only support the 'hwaddr' option. "\
- "'%s=%s' found on machine '%s' "\
+ "only support the 'hwaddr' and 'driver' "\
+ "option. '%s=%s' found on machine '%s' "\
"interface '%s'" % (name, val,
m_id, if_id)
raise MapperError(msg)
--
1.8.3.1
9 years, 10 months
[PATCH] VirtUtils: use libvirt bindings instead of virsh
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
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>
---
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")
--
1.8.3.1
9 years, 10 months