# # Copyright 2009 Red Hat, Inc. # Cole Robinson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. """ Classes for building and installing libvirt interface xml """ import libvirt import logging from virtinst import util class Interface(object): """ Base class for building any libvirt interface object. Mostly meaningless to directly instantiate. """ INTERFACE_TYPE_BRIDGE = "bridge" INTERFACE_TYPE_BOND = "bond" INTERFACE_TYPE_ETHERNET = "ethernet" INTERFACE_TYPE_VLAN = "vlan" INTERFACE_TYPES = [INTERFACE_TYPE_BRIDGE, INTERFACE_TYPE_BOND, INTERFACE_TYPE_ETHERNET, INTERFACE_TYPE_VLAN] INTERFACE_START_MODE_NONE = "none" INTERFACE_START_MODE_ONBOOT = "onboot" INTERFACE_START_MODE_HOTPLUG = "hotplug" INTERFACE_START_MODES = [INTERFACE_START_MODE_NONE, INTERFACE_START_MODE_ONBOOT, INTERFACE_START_MODE_HOTPLUG] @staticmethod def interface_class_for_type(interface_type): if interface_type not in Interface.INTERFACE_TYPES: raise ValueError("Unknown interface type '%s'" % interface_type) if interface_type == Interface.INTERFACE_TYPE_BRIDGE: return InterfaceBridge elif interface_type == Interface.INTERFACE_TYPE_BOND: return InterfaceBond elif interface_type == Interface.INTERFACE_TYPE_ETHERNET: return InterfaceEthernet elif interface_type == Interface.INTERFACE_TYPE_VLAN: return InterfaceVLAN else: raise ValueError("No class for interface type '%s'" % interface_type) @staticmethod def find_free_name(conn, prefix): """ Generate an unused interface name based on prefix. For example, if prefix="br", we find the first unused name such as "br0", "br1", etc. """ return util.generate_name(prefix, conn.interfaceLookupByName, sep="", force_num=True) def __init__(self, conn, object_type, name): """ Initialize object parameters """ if object_type not in self.INTERFACE_TYPES: raise ValueError("Unknown interface object type: %s" % object_type) self._object_type = object_type self._conn = conn self._name = None self._mtu = None self._macaddr = None self._start_mode = None self._protocols = [] self._protocol_xml = None self.name = name # Initialize all optional properties self._perms = None ## Properties def _get_object_type(self): return self._object_type object_type = property(_get_object_type) def _get_conn(self): return self._conn conn = property(_get_conn) def _get_name(self): return self._name def _set_name(self, val): util.validate_name(_("Interface name"), val) self._check_name_collision(val) self._name = val name = property(_get_name, _set_name, doc=_("Name for the interface object.")) def _get_mtu(self): return self._mtu def _set_mtu(self, val): self._mtu = val mtu = property(_get_mtu, _set_mtu, doc=_("Maximum transmit size in bytes")) def _get_macaddr(self): return self._macaddr def _set_macaddr(self, val): util.validate_macaddr(val) self._macaddr = val macaddr = property(_get_macaddr, _set_macaddr, doc=_("Interface MAC address")) def _get_start_mode(self): return self._start_mode def _set_start_mode(self, val): if val not in self.INTERFACE_START_MODES: raise ValueError(_("Unknown start mode '%s") % val) self._start_mode = val start_mode = property(_get_start_mode, _set_start_mode, doc=_("When the interface will be auto-started.")) def _get_protocols(self): return self._protocols def _set_protocols(self, val): self._protocols = val protocols = property(_get_protocols, _set_protocols, doc=_("Network protocol configuration")) def _get_protocol_xml_attr(self): return self._protocol_xml def _set_protocol_xml_attr(self, val): self._protocol_xml = val protocol_xml = property(_get_protocol_xml_attr, _set_protocol_xml_attr, doc="String of XML to use in place of " "generated protocol XML. This can be " "parsed from an existing interface for " "example.") def _check_name_collision(self, name): try: self.conn.interfaceLookupByName(name) except libvirt.libvirtError: return raise ValueError(_("Name '%s' already in use by another interface.") % name) # XML Building def _get_protocol_xml(self): """ Returns IP protocol XML """ if self.protocol_xml is not None: return self.protocol_xml xml = "" for p in self.protocols: xml += p.get_xml_config() return xml def _get_interface_xml(self): """ Returns the bridge/bond/... specific xml blob """ raise NotImplementedError("Must be implemented in subclass") def get_xml_config(self): """ Construct the xml description of the interface object @returns: xml description @rtype: C{str} """ xml = "" xml += "\n""" % (self.object_type, self.name) if self.start_mode: xml += " \n" % self.start_mode if self.macaddr: xml += " \n" % self.macaddr if self.mtu is not None: xml += " \n" % str(self.mtu) xml += self._get_protocol_xml() xml += self._get_interface_xml() xml += "\n" return xml def install(self, meter=None, create=True): """ Install network interface xml. """ xml = self.get_xml_config() logging.debug("Creating interface '%s' with xml:\n%s", self.name, xml) try: iface = self.conn.interfaceDefineXML(xml, 0) except Exception, e: raise RuntimeError(_("Could not define interface: %s" % str(e))) errmsg = None if create and not errmsg: try: iface.create(0) except Exception, e: errmsg = _("Could not create interface: %s" % str(e)) if errmsg: # Try and clean up the leftover pool try: iface.undefine() except Exception, e: logging.debug("Error cleaning up interface after failure: " + "%s" % str(e)) raise RuntimeError(errmsg) return iface class _InterfaceCompound(Interface): """ Class representing an interface which can have child interfaces """ def __init__(self, conn, interface_type, name): Interface.__init__(self, conn, interface_type, name) self._interfaces = [] def _get_interfaces(self): return self._interfaces def _set_interfaces(self, val): if val is not None: if type(val) is not list: raise ValueError("Interfaces must be a list or None") for i in val: if type(i) is str: # Assume this is a plain ethernet name continue if not isinstance(i, libvirt.virInterface): raise ValueError("List members must be virInterface " "instances, not %s" % type(i)) self._interfaces = val interfaces = property(_get_interfaces, _set_interfaces) def _indent_xml(self, xml, indent_size): newxml = "" for line in xml.split("\n"): if line: line = (" " * indent_size) + line + "\n" newxml += line return newxml def _get_child_interface_xml(self): xml = "" for i in self.interfaces: if type(i) is str: iface_xml = " \n" % i else: iface_xml = self._indent_xml(i.XMLDesc(0), 4) xml += iface_xml return xml def _get_interface_xml(self): raise NotImplementedError("Must be implemented in subclass") class InterfaceBridge(_InterfaceCompound): """ Class for building and installing libvirt interface bridge xml """ def __init__(self, conn, name): _InterfaceCompound.__init__(self, conn, Interface.INTERFACE_TYPE_BRIDGE, name) self._stp = None self._delay = None def _get_stp(self): return self._stp def _set_stp(self, val): if type(val) is not bool: raise ValueError("STP must be a bool value") self._stp = val stp = property(_get_stp, _set_stp, doc=_("Whether STP is enabled on the bridge")) def _get_delay(self): return self._delay def _set_delay(self, val): self._delay = val delay = property(_get_delay, _set_delay, doc=_("Delay in seconds before forwarding begins when " "joining a network.")) def _get_interface_xml(self): xml = "