commit fd3977338b84246c1d3001262f2e5d125d1cc57e Author: Radek Pazdera rpazdera@redhat.com Date: Mon Aug 13 12:01:51 2012 +0200
Common: Adding VirtUtils module
This commit adds VirtUtils module to Common.
VirtUtils is a set of classes that can be used to manage virtual domains and networks.
VirtDomainCtl can control libvirt domains, VirtNetCtl can control libvirt networks, BridgeCtl can control network bridges.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
Common/VirtUtils.py | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 242 insertions(+), 0 deletions(-) --- diff --git a/Common/VirtUtils.py b/Common/VirtUtils.py new file mode 100644 index 0000000..28a3d7e --- /dev/null +++ b/Common/VirtUtils.py @@ -0,0 +1,242 @@ +""" +Utilities for manipulating virtualization host, its guests and +connections between them + +Copyright 2012 Red Hat, Inc. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +rpazdera@redhat.com (Radek Pazdera) +""" + +import os +import re +import copy +import time +import logging +from tempfile import NamedTemporaryFile +from xml.dom import minidom +from ExecCmd import exec_cmd, ExecCmdFail +from Common.NetUtils import normalize_hwaddr, scan_netdevs + +class VirtUtilsError(Exception): + pass + +def _ip(cmd): + try: + exec_cmd("ip %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("ip command error: %s" % err) + +def _brctl(cmd): + try: + exec_cmd("brctl %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("brctl error: %s" % err) + +def _virsh(cmd): + try: + exec_cmd("virsh %s" % cmd) + except ExecCmdFail as err: + raise VirtUtilsError("virsh error: %s" % err) + + +class VirtDomainCtl: + _name = None + + def __init__(self, domain_name): + self._name = domain_name + + def start(self): + _virsh("start %s" % self._name) + + def stop(self): + _virsh("destroy %s" % self._name) + + 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) + + 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 ifdown(self, hwaddr): + _virsh("domif-setlink %s %s down" % (self._name, hwaddr)) + +class NetCtl(object): + _name = None + + def __init__(self, name): + self._name = name + + def get_name(self): + return self._name + + def init(self): + pass + + def cleanup(self): + 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 + + 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 + + leases_file.close() + return addr + + + 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) + + 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 + +class BridgeCtl(NetCtl): + _remove = False + + def __init__(self, name=None): + if not name: + name = self._generate_name() + + self._check_name(name) + self._name = name + + @staticmethod + def _check_name(name): + if len(name) > 16: + msg = "Bridge name '%s' longer than 16 characters" % name + raise VirtUtilsError(msg) + + @staticmethod + def _generate_name(): + devs = scan_netdevs() + + index = 0 + while True: + name = "lnstbr%d" % index + index += 1 + unique = True + for dev in devs: + if name == dev["name"]: + unique = False + break + + if unique: + return name + + def _exists(self): + devs = scan_netdevs() + for dev in devs: + if self._name == dev["name"]: + return True + + return False + + def init(self): + if not self._exists(): + _brctl("addbr %s" % self._name) + self._remove = True + + _ip("link set %s up" % self._name) + + def cleanup(self): + if self._remove: + _ip("link set %s down" % self._name) + _brctl("delbr %s" % self._name)
lnst-developers@lists.fedorahosted.org