class VirtualDeviceMaster(XMLBuilderDomain): def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) self._startport = None def parse_friendly_master(self, masterstr): try: int(masterstr) self._startport = masterstr except: logging.exception("Error parsing device master.") return None def _get_startport(self): return self._startport def _set_startport(self, val): self._startport = val startport = _xml_property(_get_startport, _set_startport, xpath="./master/@startport") def _get_xml_config(self): if self.startport is None: return return "<master startport='%s'/>" % self.startport
def _get_mode_prop(channel_type): # pylint: disable=W0212 xpath = "./channel[@name='%s']/@mode" % channel_type def get_mode(s): return s._channels.get(channel_type, None) def set_mode(s, val): s._channels[channel_type] = val return _xml_property(get_mode, set_mode, xpath=xpath)
class CPUFeature(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <cpu> child <feature> XML """ POLICIES = ["force", "require", "optional", "disable", "forbid"] def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) self._name = None self._policy = None if self._is_parse(): return def _get_name(self): return self._name def _set_name(self, val): self._name = val name = _xml_property(_get_name, _set_name, xpath="./@name") def _get_policy(self): return self._policy def _set_policy(self, val): self._policy = val policy = _xml_property(_get_policy, _set_policy, xpath="./@policy") def _get_xml_config(self): if not self.name: return "" xml = " <feature" if self.policy: xml += " policy='%s'" % self.policy xml += " name='%s'/>" % self.name return xml
class VirtualAudio(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_AUDIO MODEL_DEFAULT = "default" MODELS = ["es1370", "sb16", "pcspk", "ac97", "ich6", MODEL_DEFAULT] def __init__(self, model=None, conn=None, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._model = None if self._is_parse(): return if model is None: model = self.MODEL_DEFAULT self.model = model def get_model(self): return self._model def set_model(self, new_model): if type(new_model) != str: raise ValueError( _("'model' must be a string, " " was '%s'." % type(new_model))) if not self.MODELS.count(new_model): raise ValueError(_("Unsupported sound model '%s'" % new_model)) self._model = new_model model = _xml_property(get_model, set_model, xpath="./@model") def _get_xml_config(self): model = self.model if model == self.MODEL_DEFAULT: model = "es1370" return " <sound model='%s'/>" % model
class VirtualDeviceAlias(XMLBuilderDomain): def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) self._name = None def _get_name(self): return self._name def _set_name(self, val): self._name = val name = _xml_property(_get_name, _set_name, xpath="./alias/@name") def _get_xml_config(self): return ""
class VirtualMemballoon(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_MEMBALLOON MODEL_DEFAULT = "virtio" MODELS = ["xen", "none", MODEL_DEFAULT] def __init__(self, conn=None, model=MODEL_DEFAULT, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._model = None if self._is_parse(): return self.model = model def get_model(self): return self._model def set_model(self, new_model): if type(new_model) != str: raise ValueError( _("'model' must be a string, " " was '%s'." % type(new_model))) if not self.MODELS.count(new_model): raise ValueError(_("Unsupported memballoon model '%s'" % new_model)) self._model = new_model model = _xml_property(get_model, set_model, xpath="./@model") def _get_xml_config(self): xml = " <memballoon model='%s'" % self.model xml += "/>" return xml
class Clock(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <clock> XML """ _dumpxml_xpath = "/domain/clock" def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) self._offset = None def get_offset(self): return self._offset def set_offset(self, val): self._offset = val offset = _xml_property(get_offset, set_offset, xpath="./clock/@offset") def _get_xml_config(self): if not self.offset: return "" return """ <clock offset="%s"/>""" % self.offset
class VirtualSmartCardDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_SMARTCARD # Default models list MODE_DEFAULT = "passthrough" _modes = ["passthrough", "host-certificates", "host"] TYPE_DEFAULT = "tcp" _types = ["tcp", "spicevmc", None] def __init__(self, conn, mode=MODE_DEFAULT, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._mode = None self._type = None if self._is_parse(): return self.mode = mode def get_modes(self): return self._modes[:] modes = property(get_modes) def get_mode(self): return self._mode def set_mode(self, val): if val not in self.modes: raise ValueError(_("Unknown smartcard mode '%s'") % val) self._mode = val mode = _xml_property(get_mode, set_mode, xpath="./@mode") def get_types(self): return self._types[:] types = property(get_types) def get_type(self): if self._type is None and self.mode == "passthrough": return "spicevmc" return self._type def set_type(self, val): if val not in self.types: raise ValueError(_("Unknown smartcard type '%s'") % val) self._type = val type = _xml_property(get_type, set_type, xpath="./@type") def _get_xml_config(self): mode = self.mode xml = " <smartcard mode='%s'" % mode if self.type: xml += " type='%s'" % self.type xml += ">\n" xml += " </smartcard>" return xml
class VirtualGraphics(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_GRAPHICS TYPE_SDL = "sdl" TYPE_VNC = "vnc" TYPE_RDP = "rdp" TYPE_SPICE = "spice" types = [TYPE_VNC, TYPE_SDL, TYPE_RDP, TYPE_SPICE] CHANNEL_TYPE_MAIN = "main" CHANNEL_TYPE_DISPLAY = "display" CHANNEL_TYPE_INPUTS = "inputs" CHANNEL_TYPE_CURSOR = "cursor" CHANNEL_TYPE_PLAYBACK = "playback" CHANNEL_TYPE_RECORD = "record" channel_types = [CHANNEL_TYPE_MAIN, CHANNEL_TYPE_DISPLAY, CHANNEL_TYPE_INPUTS, CHANNEL_TYPE_CURSOR, CHANNEL_TYPE_PLAYBACK, CHANNEL_TYPE_RECORD] CHANNEL_MODE_SECURE = "secure" CHANNEL_MODE_INSECURE = "insecure" CHANNEL_MODE_ANY = "any" channel_modes = [CHANNEL_MODE_SECURE, CHANNEL_MODE_INSECURE, CHANNEL_MODE_ANY] KEYMAP_LOCAL = "local" KEYMAP_DEFAULT = "default" _special_keymaps = [KEYMAP_LOCAL, KEYMAP_DEFAULT] @staticmethod def valid_keymaps(): """ Return a list of valid keymap values. """ from virtinst import hostkeymap orig_list = hostkeymap.keytable.values() sort_list = [] orig_list.sort() for k in orig_list: if k not in sort_list: sort_list.append(k) return sort_list @staticmethod def pretty_type_simple(gtype): if (gtype in [VirtualGraphics.TYPE_VNC, VirtualGraphics.TYPE_SDL, VirtualGraphics.TYPE_RDP]): return str(gtype).upper() return str(gtype).capitalize() def __init__(self, type=TYPE_VNC, port=-1, listen=None, passwd=None, keymap=KEYMAP_DEFAULT, conn=None, parsexml=None, parsexmlnode=None, tlsPort=-1, channels=None, caps=None, passwdValidTo=None): # pylint: disable=W0622 # Redefining built-in 'type', but it matches the XML so keep it VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._port = None self._tlsPort = None self._listen = None self._passwd = None self._passwdValidTo = None self._keymap = None self._xauth = None self._display = None self._socket = None self._channels = {} self._local_keymap = -1 if self._is_parse(): return self.type = type self.port = port self.tlsPort = tlsPort self.keymap = keymap self.listen = listen self.passwd = passwd self.passwdValidTo = passwdValidTo if channels: self.channels = channels def _cache(self): # Make sure we've cached the _local_keymap value before copy() self._default_keymap() def _default_keymap(self, force_local=False): if (not force_local and self.conn and support.check_conn_support(self.conn, support.SUPPORT_CONN_KEYMAP_AUTODETECT)): return None if self._local_keymap == -1: from virtinst import hostkeymap self._local_keymap = hostkeymap.default_keymap() return self._local_keymap def get_type(self): return self._type def set_type(self, val): if val not in self.types: raise ValueError(_("Unknown graphics type '%s'") % val) self._type = val type = _xml_property(get_type, set_type, xpath="./@type") def _get_xauth(self): return self._xauth def _set_xauth(self, val): self._xauth = val xauth = _xml_property(_get_xauth, _set_xauth, xpath="./@xauth") def _get_display(self): return self._display def _set_display(self, val): self._display = val display = _xml_property(_get_display, _set_display, xpath="./@display") def get_keymap(self): if self._keymap == self.KEYMAP_DEFAULT: return self._default_keymap() if self._keymap == self.KEYMAP_LOCAL: return self._default_keymap(force_local=True) return self._keymap def set_keymap(self, val): # At this point, 'None' is a valid value if val is None: self._keymap = None return if val in self._special_keymaps: self._keymap = val return if type(val) is not str: raise ValueError(_("Keymap must be a string")) if val.lower() == self.KEYMAP_LOCAL: val = self._default_keymap(force_local=True) elif len(val) > 16: raise ValueError(_("Keymap must be less than 16 characters")) elif re.match("^[a-zA-Z0-9_-]*$", val) is None: raise ValueError(_("Keymap can only contain alphanumeric, " "'_', or '-' characters")) self._keymap = val keymap = _xml_property(get_keymap, set_keymap, xpath="./@keymap") def get_port(self): return self._port def set_port(self, val): if val is None: val = -1 try: val = int(val) except: pass if (type(val) is not int or (val != -1 and (val < 5900 or val > 65535))): raise ValueError(_("VNC port must be a number between " "5900 and 65535, or -1 for auto allocation")) self._port = val port = _xml_property(get_port, set_port, get_converter=lambda s, x: int(x or -1), xpath="./@port") def get_listen(self): return self._listen def set_listen(self, val): self._listen = val listen = _xml_property(get_listen, set_listen, xpath="./@listen") def get_passwd(self): return self._passwd def set_passwd(self, val): self._passwd = val passwd = _xml_property(get_passwd, set_passwd, xpath="./@passwd") def get_passwdValidTo(self): return self._passwdValidTo def set_passwdValidTo(self, val): self._passwdValidTo = val passwdValidTo = _xml_property(get_passwdValidTo, set_passwdValidTo, xpath="./@passwdValidTo") def _get_socket(self): return self._socket def _set_socket(self, val): self._socket = val socket = _xml_property(_get_socket, _set_socket, xpath="./@socket") def get_tlsPort(self): return self._tlsPort def set_tlsPort(self, val): if val is None: val = -1 try: val = int(val) except: pass if (type(val) is not int or (val != -1 and (val < 5900 or val > 65535))): raise ValueError(_("TLS port must be a number between " "5900 and 65535, or -1 for auto allocation")) self._tlsPort = val tlsPort = _xml_property(get_tlsPort, set_tlsPort, get_converter=lambda s, x: int(x or -1), xpath="./@tlsPort") channel_main_mode = _get_mode_prop(CHANNEL_TYPE_MAIN) channel_display_mode = _get_mode_prop(CHANNEL_TYPE_DISPLAY) channel_inputs_mode = _get_mode_prop(CHANNEL_TYPE_INPUTS) channel_cursor_mode = _get_mode_prop(CHANNEL_TYPE_CURSOR) channel_playback_mode = _get_mode_prop(CHANNEL_TYPE_PLAYBACK) channel_record_mode = _get_mode_prop(CHANNEL_TYPE_RECORD) def _build_xml(self, port=None, listen=None, keymap=None, passwd=None, display=None, xauth=None, tlsPort=None, canautoport=False, passwdValidTo=None, socket=None): doautoport = (canautoport and (port in [None, -1] and tlsPort in [None, -1])) portxml = (port is not None and (" port='%d'" % port) or "") tlsportxml = (tlsPort is not None and (" tlsPort='%d'" % tlsPort) or "") autoportxml = (doautoport and " autoport='yes'" or "") keymapxml = (keymap and (" keymap='%s'" % keymap) or "") listenxml = (listen and (" listen='%s'" % listen) or "") passwdxml = (passwd and (" passwd='%s'" % passwd) or "") passwdValidToxml = (passwdValidTo and (" passwdValidTo='%s'" % passwdValidTo) or "") xauthxml = (xauth and (" xauth='%s'" % xauth) or "") displayxml = (display and (" display='%s'" % display) or "") socketxml = (socket and (" socket='%s'" % socket) or "") xml = (" " + "<graphics type='%s'" % self.type + portxml + tlsportxml + autoportxml + keymapxml + listenxml + passwdxml + passwdValidToxml + socketxml + displayxml + xauthxml + "/>") return xml def _sdl_config(self): if "DISPLAY" not in os.environ and not self.display: raise RuntimeError("No DISPLAY environment variable set.") disp = self.display or os.environ["DISPLAY"] xauth = self.xauth or os.path.expanduser("~/.Xauthority") return self._build_xml(display=disp, xauth=xauth) def _spice_config(self): return self._build_xml(port=self.port, keymap=self.keymap, passwd=self.passwd, listen=self.listen, tlsPort=self.tlsPort, canautoport=True, passwdValidTo=self.passwdValidTo) def _vnc_config(self): return self._build_xml(port=self.port, keymap=self.keymap, passwd=self.passwd, listen=self.listen, # VNC supports autoport, but use legacy # syntax to not break XML tests canautoport=False, passwdValidTo=self.passwdValidTo, socket=self.socket) def _get_xml_config(self): if self._type == self.TYPE_SDL: return self._sdl_config() if self._type == self.TYPE_SPICE: return self._spice_config() if self._type == self.TYPE_VNC: return self._vnc_config() else: raise ValueError(_("Unknown graphics type"))
class CPU(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <cpu> XML """ _dumpxml_xpath = "/domain/cpu" MATCHS = ["minimum", "exact", "strict"] def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): self._model = None self._match = None self._vendor = None self._mode = None self._features = [] self._sockets = None self._cores = None self._threads = None XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) if self._is_parse(): return def _parsexml(self, xml, node): XMLBuilderDomain.XMLBuilderDomain._parsexml(self, xml, node) for node in self._xml_node.children: if node.name != "feature": continue feature = CPUFeature(self.conn, parsexmlnode=node) self._features.append(feature) def _get_features(self): return self._features[:] features = _xml_property(_get_features) def add_feature(self, name, policy="require"): feature = CPUFeature(self.conn) feature.name = name feature.policy = policy if self._is_parse(): xml = feature.get_xml_config() node = libxml2.parseDoc(xml).children feature.set_xml_node(node) self._add_child_node("./cpu", node) self._features.append(feature) def remove_feature(self, feature): if self._is_parse() and feature in self._features: xpath = feature.get_xml_node_path() if xpath: self._remove_child_xpath(xpath) self._features.remove(feature) def _get_model(self): return self._model def _set_model(self, val): if val: self.mode = "custom" if val and not self.match: self.match = "exact" self._model = val model = _xml_property(_get_model, _set_model, xpath="./cpu/model") def _get_match(self): return self._match def _set_match(self, val): self._match = val match = _xml_property(_get_match, _set_match, xpath="./cpu/@match") def _get_vendor(self): return self._vendor def _set_vendor(self, val): self._vendor = val vendor = _xml_property(_get_vendor, _set_vendor, xpath="./cpu/vendor") def _get_mode(self): return self._mode def _set_mode(self, val): self._mode = val mode = _xml_property(_get_mode, _set_mode, xpath="./cpu/@mode") # Topology properties def _get_sockets(self): return self._sockets def _set_sockets(self, val): self._sockets = _int_or_none(val) sockets = _xml_property(_get_sockets, _set_sockets, get_converter=lambda s, x: _int_or_none(x), xpath="./cpu/topology/@sockets") def _get_cores(self): return self._cores def _set_cores(self, val): self._cores = _int_or_none(val) cores = _xml_property(_get_cores, _set_cores, get_converter=lambda s, x: _int_or_none(x), xpath="./cpu/topology/@cores") def _get_threads(self): return self._threads def _set_threads(self, val): self._threads = _int_or_none(val) threads = _xml_property(_get_threads, _set_threads, get_converter=lambda s, x: _int_or_none(x), xpath="./cpu/topology/@threads") def clear_attrs(self): self.match = None self.mode = None self.vendor = None self.model = None for feature in self.features: self.remove_feature(feature) def copy_host_cpu(self): """ Enact the equivalent of qemu -cpu host, pulling all info from capabilities about the host CPU """ cpu = self._get_caps().host.cpu if not cpu.model: raise ValueError(_("No host CPU reported in capabilities")) self.mode = "custom" self.match = "exact" self.model = cpu.model self.vendor = cpu.vendor for feature in self.features: self.remove_feature(feature) for name in cpu.features.names(): self.add_feature(name) def vcpus_from_topology(self): """ Determine the CPU count represented by topology, or 1 if no topology is set """ self.set_topology_defaults() if self.sockets: return self.sockets * self.cores * self.threads return 1 def set_topology_defaults(self, vcpus=None): """ Fill in unset topology values, using the passed vcpus count if required """ if (self.sockets is None and self.cores is None and self.threads is None): return if vcpus is None: if self.sockets is None: self.sockets = 1 if self.threads is None: self.threads = 1 if self.cores is None: self.cores = 1 vcpus = int(vcpus or 0) if not self.sockets: if not self.cores: self.sockets = vcpus / self.threads else: self.sockets = vcpus / self.cores if not self.cores: if not self.threads: self.cores = vcpus / self.sockets else: self.cores = vcpus / (self.sockets * self.threads) if not self.threads: self.threads = vcpus / (self.sockets * self.cores) return def _get_topology_xml(self): xml = "" if self.sockets: xml += " sockets='%s'" % self.sockets if self.cores: xml += " cores='%s'" % self.cores if self.threads: xml += " threads='%s'" % self.threads if not xml: return "" return " <topology%s/>\n" % xml def _get_feature_xml(self): xml = "" for feature in self._features: xml += feature.get_xml_config() + "\n" return xml def _get_xml_config(self): top_xml = self._get_topology_xml() feature_xml = self._get_feature_xml() mode_xml = "" match_xml = "" if self.match: match_xml = " match='%s'" % self.match xml = "" if self.model == "host-passthrough": self.mode = "host-passthrough" mode_xml = " mode='%s'" % self.mode xml += " <cpu%s/>" % mode_xml return xml else: self.mode = "custom" mode_xml = " mode='%s'" % self.mode if not (self.model or top_xml or feature_xml): return "" # Simple topology XML mode xml += " <cpu%s%s>\n" % (mode_xml, match_xml) if self.model: xml += " <model>%s</model>\n" % self.model if self.vendor: xml += " <vendor>%s</vendor>\n" % self.vendor if top_xml: xml += top_xml if feature_xml: xml += feature_xml xml += " </cpu>" return xml
class VirtualRNGDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_RNG TYPE_RANDOM = "random" TYPE_EGD = "egd" TYPES = [TYPE_RANDOM, TYPE_EGD] BACKEND_TYPE_UDP = "udp" BACKEND_TYPE_TCP = "tcp" BACKEND_TYPES = [BACKEND_TYPE_UDP, BACKEND_TYPE_TCP] BACKEND_MODE_BIND = "bind" BACKEND_MODE_CONNECT = "connect" BACKEND_MODES = [BACKEND_MODE_BIND, BACKEND_MODE_CONNECT] def __init__(self, conn=None, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._model = None self._backend_type = None self._bind_host = None self._bind_service = None self._connect_host = None self._connect_service = None self._rate_bytes = None self._rate_period = None self._device = None if self._is_parse(): return @staticmethod def get_pretty_type(rng_type): if rng_type == VirtualRNGDevice.TYPE_RANDOM: return _("Random") if rng_type == VirtualRNGDevice.TYPE_EGD: return _("Entropy Gathering Daemon") return rng_type @staticmethod def get_pretty_backend_type(backend_type): return {"udp": "UDP", "tcp": "TCP"}.get(backend_type) or backend_type @staticmethod def get_pretty_mode(mode): return {"bind": "Bind", "connect": "Connect"}.get(mode) or mode def backend_mode(self): ret = [] if self.bind_host or self.bind_service: ret.append(VirtualRNGDevice.BACKEND_MODE_BIND) if self.connect_host or self.connect_service: ret.append(VirtualRNGDevice.BACKEND_MODE_CONNECT) return ret def supports_property(self, propname): """ Whether the rng dev type supports the passed property name """ users = { "type": [self.TYPE_EGD, self.TYPE_RANDOM], "model": [self.TYPE_EGD, self.TYPE_RANDOM], "bind_host": [self.TYPE_EGD], "bind_service": [self.TYPE_EGD], "connect_host": [self.TYPE_EGD], "connect_service": [self.TYPE_EGD], "backend_type": [self.TYPE_EGD], "device": [self.TYPE_RANDOM], "rate_bytes": [self.TYPE_EGD, self.TYPE_RANDOM], "rate_period": [self.TYPE_EGD, self.TYPE_RANDOM], } if users.get(propname): return self.type in users[propname] return hasattr(self, propname) def _get_type(self): return self._type def _set_type(self, m): self._type = m type = _xml_property(_get_type, _set_type, xpath="./backend/@model") def _get_model(self): return self._model def _set_model(self, m): self._model = m model = _xml_property(_get_model, _set_model, xpath="./@model") def _get_backend_type(self): return self._backend_type def _set_backend_type(self, t): self._backend_type = t backend_type = _xml_property(_get_backend_type, _set_backend_type, xpath="./backend/@type") def _get_bind_host(self): return self._bind_host def _set_bind_host(self, m): self._bind_host = m bind_host = _xml_property(_get_bind_host, _set_bind_host, xpath="./backend/source[@mode='bind']/@host") def _get_connect_host(self): return self._connect_host def _set_connect_host(self, m): self._connect_host = m connect_host = _xml_property( _get_connect_host, _set_connect_host, xpath="./backend/source[@mode='connect']/@host") def _get_bind_service(self): return self._bind_service def _set_bind_service(self, m): self._bind_service = m bind_service = _xml_property( _get_bind_service, _set_bind_service, xpath="./backend/source[@mode='bind']/@service") def _get_connect_service(self): return self._connect_service def _set_connect_service(self, m): self._connect_service = m connect_service = _xml_property( _get_connect_service, _set_connect_service, xpath="./backend/source[@mode='connect']/@service") def _get_rate_bytes(self): return self._rate_bytes def _set_rate_bytes(self, b): self._rate_bytes = b rate_bytes = _xml_property(_get_rate_bytes, _set_rate_bytes, xpath="./rate/@bytes") def _get_rate_period(self): return self._rate_period def _set_rate_period(self, p): self._rate_period = p rate_period = _xml_property(_get_rate_period, _set_rate_period, xpath="./rate/@period") def _get_device(self): if self._type == self.TYPE_RANDOM: return self._device return None def _set_device(self, d): self._device = d device = _xml_property(_get_device, _set_device, xpath="./backend") def _get_xml_config(self): rng_model = self.model or "virtio" xml = (" <rng model='%s'>\n" % rng_model) if self.rate_bytes or self.rate_period: xml += " <rate" if self.rate_period: xml += " period='%s'" % self.rate_period if self.rate_bytes: xml += " bytes='%s'" % self.rate_bytes xml += "/>\n" if self.type == self.TYPE_RANDOM: xml += " <backend model='random'>%s</backend>\n" % self.device else: model = "model='%s'" % self.type backend_type = "type='%s'" % (self.backend_type or "tcp") xml += " <backend %s %s>\n" % (model, backend_type) def add_source(mode, host, service): ret = " <source mode='%s'" % mode if host: ret += " host='%s'" % host if service: ret += " service='%s'" % service return ret + "/>\n" if self.bind_host or self.bind_service: xml += add_source("bind", self.bind_host, self.bind_service) if self.connect_host or self.connect_service: xml += add_source("connect", self.connect_host, \ self.connect_service) xml += " </backend>\n" xml += " </rng>" return xml
class Installer(XMLBuilderDomain.XMLBuilderDomain): """ Installer classes attempt to encapsulate all the parameters needed to 'install' a guest: essentially, booting the guest with the correct media for the OS install phase (if there is one), and setting up the guest to boot to the correct media for all subsequent runs. Some of the actual functionality: - Determining what type of install media has been requested, and representing it correctly to the Guest - Fetching install kernel/initrd or boot.iso from a URL - Setting the boot device as appropriate depending on whether we are booting into an OS install, or booting post-install Some of the information that the Installer needs to know to accomplish this: - Install media location (could be a URL, local path, ...) - Virtualization type (parameter 'os_type') ('xen', 'hvm', etc.) - Hypervisor name (parameter 'type') ('qemu', 'kvm', 'xen', etc.) - Guest architecture ('i686', 'x86_64') """ _dumpxml_xpath = "/domain/os" _has_install_phase = True def __init__(self, type="xen", location=None, extraargs=None, os_type=None, conn=None, parsexml=None, parsexmlnode=None, caps=None): # pylint: disable=W0622 # Redefining built-in 'type', but it matches the XML so keep it XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) self._type = None self._location = None self._initrd_injections = [] self._cdrom = False self._os_type = None self._scratchdir = None self._arch = None self._machine = None self._loader = None self._init = None self._install_bootconfig = Boot(self.conn) self._bootconfig = Boot(self.conn, parsexml, parsexmlnode) # Devices created/added during the prepare() stage self.install_devices = [] if self._is_parse(): return # FIXME: Better solution? Skip validating this since we may not be # able to install a VM of the host arch if self._get_caps(): self._arch = self._get_caps().host.arch if type is None: type = "xen" self.type = type if not os_type is None: self.os_type = os_type else: self.os_type = "xen" if not location is None: self.location = location self.extraargs = extraargs self._tmpfiles = [] self._tmpvols = [] def get_conn(self): return self._conn conn = property(get_conn) def _get_bootconfig(self): return self._bootconfig bootconfig = property(_get_bootconfig) # Hypervisor name (qemu, kvm, xen, lxc, etc.) def get_type(self): return self._type def set_type(self, val): self._type = val type = _xml_property(get_type, set_type, xpath="./@type") # Virtualization type ('xen' == xen paravirt, or 'hvm) def get_os_type(self): return self._os_type def set_os_type(self, val): # Older libvirt back compat: if user specifies 'linux', convert # internally to newer equivalent value 'xen' if val == "linux": val = "xen" # XXX: Need to validate this: have some whitelist based on caps? self._os_type = val os_type = _xml_property(get_os_type, set_os_type, xpath="./os/type") def get_arch(self): return self._arch def set_arch(self, val): # XXX: Sanitize to a consisten value (i368 -> i686) # XXX: Validate against caps self._arch = val arch = _xml_property(get_arch, set_arch, xpath="./os/type/@arch") def _get_machine(self): return self._machine def _set_machine(self, val): self._machine = val machine = _xml_property(_get_machine, _set_machine, xpath="./os/type/@machine") def _get_loader(self): return self._loader def _set_loader(self, val): self._loader = val loader = _xml_property(_get_loader, _set_loader, xpath="./os/loader") def _get_init(self): return self._init def _set_init(self, val): self._init = val init = _xml_property(_get_init, _set_init, xpath="./os/init") def get_scratchdir(self): if not self.scratchdir_required(): return None if not self._scratchdir: self._scratchdir = self._get_scratchdir() logging.debug("scratchdir=%s", self._scratchdir) return self._scratchdir scratchdir = property(get_scratchdir) def get_cdrom(self): return self._cdrom def set_cdrom(self, enable): if enable not in [True, False]: raise ValueError(_("Guest.cdrom must be a boolean type")) self._cdrom = enable cdrom = property(get_cdrom, set_cdrom) def get_location(self): return self._location def set_location(self, val): self._location = val location = property(get_location, set_location) def get_initrd_injections(self): return self._initrd_injections def set_initrd_injections(self, val): self._initrd_injections = val initrd_injections = property(get_initrd_injections, set_initrd_injections) # extra arguments to pass to the guest installer def get_extra_args(self): return self._install_bootconfig.kernel_args def set_extra_args(self, val): self._install_bootconfig.kernel_args = val extraargs = property(get_extra_args, set_extra_args) # Public helper methods def scratchdir_required(self): """ Returns true if scratchdir is needed for the passed install parameters. Apps can use this to determine if they should attempt to ensure scratchdir permissions are adequate """ return False def is_hvm(self): return self.os_type == "hvm" def is_xenpv(self): return self.os_type in ["xen", "linux"] def is_container(self): return self.os_type == "exe" # Private methods def _get_system_scratchdir(self): if platform.system() == "SunOS": return "/var/tmp" if self.type == "test": return "/tmp" elif self.type == "xen": return XEN_SCRATCH else: return LIBVIRT_SCRATCH def _get_scratchdir(self): scratch = None if not self.is_session_uri(): scratch = self._get_system_scratchdir() if (not scratch or not os.path.exists(scratch) or not os.access(scratch, os.W_OK)): scratch = os.path.expanduser("~/.virtinst/boot") if not os.path.exists(scratch): os.makedirs(scratch, 0751) return scratch def _get_bootdev(self, isinstall, guest): raise NotImplementedError("Must be implemented in subclass") def _build_boot_order(self, isinstall, guest): bootorder = [self._get_bootdev(isinstall, guest)] # If guest has an attached disk, always have 'hd' in the boot # list, so disks are marked as bootable/installable (needed for # windows virtio installs, and booting local disk from PXE) for disk in guest.get_devices("disk"): if disk.device == disk.DEVICE_DISK: bootdev = self.bootconfig.BOOT_DEVICE_HARDDISK if bootdev not in bootorder: bootorder.append(bootdev) break return bootorder def _get_default_init(self, guest): if not self.is_container(): return for fs in guest.get_devices("filesystem"): if fs.target == "/": return "/sbin/init" return "/bin/sh" def _get_osblob_helper(self, guest, isinstall, bootconfig): conn = guest.conn arch = self.arch machine = self.machine hvtype = self.type loader = self.loader os_type = self.os_type init = self.init or self._get_default_init(guest) hvxen = (hvtype == "xen") if not loader and self.is_hvm() and hvxen: loader = "/usr/lib/xen/boot/hvmloader" # Use older libvirt 'linux' value for back compat if os_type == "xen" and hvxen: os_type = "linux" if (not isinstall and self.is_xenpv() and not self.bootconfig.kernel): return "<bootloader>%s</bootloader>" % _pygrub_path(conn) osblob = "<os>" typexml = " <type" if arch: typexml += " arch='%s'" % arch if machine: typexml += " machine='%s'" % machine typexml += ">%s</type>" % os_type osblob = util.xml_append(osblob, typexml) if init: osblob = util.xml_append( osblob, " <init>%s</init>" % util.xml_escape(init)) if loader: osblob = util.xml_append( osblob, " <loader>%s</loader>" % util.xml_escape(loader)) if not self.is_container(): osblob = util.xml_append(osblob, bootconfig.get_xml_config()) osblob = util.xml_append(osblob, " </os>") return osblob # Method definitions def _get_xml_config(self, guest, isinstall): """ Generate the portion of the guest xml that determines boot devices and parameters. (typically the <os></os> block) @param guest: Guest instance we are installing @type guest: L{Guest} @param isinstall: Whether we want xml for the 'install' phase or the 'post-install' phase. @type isinstall: C{bool} """ # pylint: disable=W0221 # Argument number differs from overridden method if isinstall: bootconfig = self._install_bootconfig else: bootconfig = self.bootconfig if isinstall and not self.has_install_phase(): return bootorder = self._build_boot_order(isinstall, guest) bootconfig = copy.copy(bootconfig) if not bootconfig.bootorder: bootconfig.bootorder = bootorder return self._get_osblob_helper(guest, isinstall, bootconfig) def has_install_phase(self): """ Return True if the requested setup is actually installing an OS into the guest. Things like LiveCDs, Import, or a manually specified bootorder do not have an install phase. """ return self._has_install_phase def cleanup(self): """ Remove any temporary files retrieved during installation """ for f in self._tmpfiles: logging.debug("Removing " + f) os.unlink(f) for vol in self._tmpvols: logging.debug("Removing volume '%s'", vol.name()) vol.delete(0) self._tmpvols = [] self._tmpfiles = [] self.install_devices = [] def prepare(self, guest, meter): """ Fetch any files needed for installation. @param guest: guest instance being installed @type guest: L{Guest} @param meter: progress meter @type meter: Urlgrabber ProgressMeter """ raise NotImplementedError("Must be implemented in subclass") def detect_distro(self): """ Attempt to detect the distro for the Installer's 'location'. If an error is encountered in the detection process (or if detection is not relevant for the Installer type), (None, None) is returned @returns: (distro type, distro variant) tuple """ return (None, None) def guest_from_installer(self): """ Return a L{Guest} instance wrapping the current installer. If all the appropriate values are present in the installer (conn, type, os_type, arch, machine), we have everything we need to determine what L{Guest} class is expected and what default values to pass it. This is a convenience method to save the API user from having to enter all these known details twice. """ if not self.conn: raise ValueError(_("A connection must be specified.")) guest, domain = CapabilitiesParser.guest_lookup(conn=self.conn, caps=self._get_caps(), os_type=self.os_type, typ=self.type, arch=self.arch, machine=self.machine) gobj = virtinst.Guest(installer=self, conn=self.conn) gobj.arch = guest.arch gobj.emulator = domain.emulator self.loader = domain.loader return gobj
class VirtualWatchdog(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_WATCHDOG MODEL_DEFAULT = "default" MODELS = ["i6300esb", "ib700", MODEL_DEFAULT] ACTION_DEFAULT = "default" ACTION_SHUTDOWN = "shutdown" ACTION_RESET = "reset" ACTION_POWEROFF = "poweroff" ACTION_PAUSE = "pause" ACTION_NONE = "none" ACTIONS = [ ACTION_RESET, ACTION_SHUTDOWN, ACTION_POWEROFF, ACTION_PAUSE, ACTION_NONE, ACTION_DEFAULT ] @staticmethod def get_action_desc(action): if action == VirtualWatchdog.ACTION_RESET: return _("Forcefully reset the guest") if action == VirtualWatchdog.ACTION_SHUTDOWN: return _("Gracefully shutdown the guest") if action == VirtualWatchdog.ACTION_POWEROFF: return _("Forcefully power off the guest") if action == VirtualWatchdog.ACTION_PAUSE: return _("Pause the guest") if action == VirtualWatchdog.ACTION_NONE: return _("No action") if action == VirtualWatchdog.ACTION_DEFAULT: return _("Hypervisor default") else: return action def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._model = None self._action = None if self._is_parse(): return self.model = self.MODEL_DEFAULT self.action = self.ACTION_DEFAULT def get_model(self): return self._model def set_model(self, new_model): if type(new_model) != str: raise ValueError( _("'model' must be a string, " " was '%s'." % type(new_model))) if not self.MODELS.count(new_model): raise ValueError(_("Unsupported watchdog model '%s'" % new_model)) self._model = new_model model = _xml_property(get_model, set_model, xpath="./@model") def get_action(self): return self._action def set_action(self, val): if val not in self.ACTIONS: raise ValueError("Unknown watchdog action '%s'." % val) self._action = val action = _xml_property(get_action, set_action, xpath="./@action") def _get_xml_config(self): model = self.model if model == self.MODEL_DEFAULT: model = "i6300esb" action = self.action if action == self.ACTION_DEFAULT: action = self.ACTION_RESET xml = " <watchdog model='%s'" % model if action: xml += " action='%s'" % action xml += "/>" return xml
class DomainNumatune(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <numatune> XML """ @staticmethod def validate_cpuset(conn, val): if val is None or val == "": return if not isinstance(val, str) or len(val) == 0: raise ValueError(_("cpuset must be string")) if re.match("^[0-9,-^]*$", val) is None: raise ValueError( _("cpuset can only contain numeric, ',', '^', or " "'-' characters")) pcpus = get_phy_cpus(conn) for c in val.split(','): # Redundant commas if not c: continue if "-" in c: (x, y) = c.split('-', 1) x = int(x) y = int(y) if x > y: raise ValueError(_("cpuset contains invalid format.")) if x >= pcpus or y >= pcpus: raise ValueError( _("cpuset's pCPU numbers must be less " "than pCPUs.")) else: if c.startswith("^"): c = c[1:] c = int(c) if c >= pcpus: raise ValueError( _("cpuset's pCPU numbers must be less " "than pCPUs.")) @staticmethod def cpuset_str_to_tuple(conn, cpuset): DomainNumatune.validate_cpuset(conn, cpuset) pinlist = [False] * get_phy_cpus(conn) entries = cpuset.split(",") for e in entries: series = e.split("-", 1) if len(series) == 1: pinlist[int(series[0])] = True continue start = int(series[0]) end = int(series[1]) for i in range(start, end + 1): pinlist[i] = True return tuple(pinlist) _dumpxml_xpath = "/domain/numatune" MEMORY_MODES = ["interleave", "strict", "preferred"] def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): self._memory_nodeset = None self._memory_mode = None XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) if self._is_parse(): return def _get_memory_nodeset(self): return self._memory_nodeset def _set_memory_nodeset(self, val): self._memory_nodeset = val memory_nodeset = _xml_property(_get_memory_nodeset, _set_memory_nodeset, xpath="./numatune/memory/@nodeset") def _get_memory_mode(self): return self._memory_mode def _set_memory_mode(self, val): self._memory_mode = val memory_mode = _xml_property(_get_memory_mode, _set_memory_mode, xpath="./numatune/memory/@mode") def _get_memory_xml(self): if not self.memory_nodeset: return "" xml = " <memory" if self.memory_mode: xml += " mode='%s'" % self.memory_mode if self.memory_nodeset: xml += " nodeset='%s'" % self.memory_nodeset xml += "/>\n" return xml def _get_xml_config(self): mem_xml = self._get_memory_xml() if not mem_xml: return "" xml = " <numatune>\n" xml += mem_xml xml += " </numatune>" return xml
class VirtualCharDevice(VirtualDevice): """ Base class for all character devices. Shouldn't be instantiated directly. """ DEV_SERIAL = "serial" DEV_PARALLEL = "parallel" DEV_CONSOLE = "console" DEV_CHANNEL = "channel" dev_types = [DEV_SERIAL, DEV_PARALLEL, DEV_CONSOLE, DEV_CHANNEL] CHAR_PTY = "pty" CHAR_DEV = "dev" CHAR_STDIO = "stdio" CHAR_PIPE = "pipe" CHAR_FILE = "file" CHAR_VC = "vc" CHAR_NULL = "null" CHAR_TCP = "tcp" CHAR_UDP = "udp" CHAR_UNIX = "unix" CHAR_SPICEVMC = "spicevmc" char_types = [CHAR_PTY, CHAR_DEV, CHAR_STDIO, CHAR_FILE, CHAR_VC, CHAR_PIPE, CHAR_NULL, CHAR_TCP, CHAR_UDP, CHAR_UNIX, CHAR_SPICEVMC] _non_channel_types = char_types[:] _non_channel_types.remove(CHAR_SPICEVMC) char_types_for_dev_type = { DEV_SERIAL: _non_channel_types, DEV_PARALLEL: _non_channel_types, DEV_CONSOLE: _non_channel_types, DEV_CHANNEL: [CHAR_SPICEVMC], } CHAR_MODE_CONNECT = "connect" CHAR_MODE_BIND = "bind" char_modes = [CHAR_MODE_CONNECT, CHAR_MODE_BIND] CHAR_PROTOCOL_RAW = "raw" CHAR_PROTOCOL_TELNET = "telnet" char_protocols = [CHAR_PROTOCOL_RAW, CHAR_PROTOCOL_TELNET] CHAR_CHANNEL_TARGET_GUESTFWD = "guestfwd" CHAR_CHANNEL_TARGET_VIRTIO = "virtio" target_types = [CHAR_CHANNEL_TARGET_GUESTFWD, CHAR_CHANNEL_TARGET_VIRTIO] CHAR_CHANNEL_ADDRESS_VIRTIO_SERIAL = "virtio-serial" address_types = [CHAR_CHANNEL_ADDRESS_VIRTIO_SERIAL] CHAR_CONSOLE_TARGET_SERIAL = "serial" CHAR_CONSOLE_TARGET_UML = "uml" CHAR_CONSOLE_TARGET_XEN = "xen" CHAR_CONSOLE_TARGET_VIRTIO = "virtio" has_target = False def get_char_type_desc(char_type): """ Return a human readable description of the passed char type """ desc = "" if char_type == VirtualCharDevice.CHAR_PTY: desc = _("Pseudo TTY") elif char_type == VirtualCharDevice.CHAR_DEV: desc = _("Physical host character device") elif char_type == VirtualCharDevice.CHAR_STDIO: desc = _("Standard input/output") elif char_type == VirtualCharDevice.CHAR_PIPE: desc = _("Named pipe") elif char_type == VirtualCharDevice.CHAR_FILE: desc = _("Output to a file") elif char_type == VirtualCharDevice.CHAR_VC: desc = _("Virtual console") elif char_type == VirtualCharDevice.CHAR_NULL: desc = _("Null device") elif char_type == VirtualCharDevice.CHAR_TCP: desc = _("TCP net console") elif char_type == VirtualCharDevice.CHAR_UDP: desc = _("UDP net console") elif char_type == VirtualCharDevice.CHAR_UNIX: desc = _("Unix socket") elif char_type == VirtualCharDevice.CHAR_SPICEVMC: desc = _("Spice agent") return desc get_char_type_desc = staticmethod(get_char_type_desc) def get_char_mode_desc(char_mode): """ Return a human readable description of the passed char type """ desc = "" if char_mode == VirtualCharDevice.CHAR_MODE_CONNECT: desc = _("Client mode") elif char_mode == VirtualCharDevice.CHAR_MODE_BIND: desc = _("Server mode") return desc get_char_mode_desc = staticmethod(get_char_mode_desc) # 'char_type' of class (must be properly set in subclass) _char_type = None def get_dev_instance(conn, dev_type, char_type): """ Set up the class attributes for the passed char_type """ # By default, all the possible parameters are enabled for the # device class. We go through here and del() all the ones that # don't apply. This is kind of whacky, but it's nice to to # allow an API user to just use hasattr(obj, paramname) to see # what parameters apply, instead of having to hardcode all that # information. if char_type == VirtualCharDevice.CHAR_PTY: c = VirtualCharPtyDevice elif char_type == VirtualCharDevice.CHAR_STDIO: c = VirtualCharStdioDevice elif char_type == VirtualCharDevice.CHAR_NULL: c = VirtualCharNullDevice elif char_type == VirtualCharDevice.CHAR_VC: c = VirtualCharVcDevice elif char_type == VirtualCharDevice.CHAR_DEV: c = VirtualCharDevDevice elif char_type == VirtualCharDevice.CHAR_FILE: c = VirtualCharFileDevice elif char_type == VirtualCharDevice.CHAR_PIPE: c = VirtualCharPipeDevice elif char_type == VirtualCharDevice.CHAR_TCP: c = VirtualCharTcpDevice elif char_type == VirtualCharDevice.CHAR_UNIX: c = VirtualCharUnixDevice elif char_type == VirtualCharDevice.CHAR_UDP: c = VirtualCharUdpDevice elif char_type == VirtualCharDevice.CHAR_SPICEVMC: c = VirtualCharSpicevmcDevice else: raise ValueError(_("Unknown character device type '%s'.") % char_type) if dev_type == VirtualCharDevice.DEV_CONSOLE: return VirtualConsoleDevice(conn) return c(conn, dev_type) get_dev_instance = staticmethod(get_dev_instance) def __init__(self, conn, dev_type, parsexml=None, parsexmlnode=None, caps=None): if dev_type not in self.dev_types: raise ValueError(_("Unknown character device type '%s'") % dev_type) self._dev_type = dev_type self._virtual_device_type = self._dev_type VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) # Init self._source_path = None self._source_mode = self.CHAR_MODE_BIND self._source_host = "127.0.0.1" self._source_port = None self._target_type = None self._target_address = None self._target_port = None self._target_name = None self._bind_host = None self._bind_port = None self._protocol = self.CHAR_PROTOCOL_RAW self._address_type = None if self.char_type == self.CHAR_UDP: self._source_mode = self.CHAR_MODE_CONNECT if self._is_parse(): return if not self._char_type: raise ValueError("Must be instantiated through a subclass.") self.char_type = self._char_type def supports_property(self, propname, ro=False): """ Whether the character dev type supports the passed property name """ users = { "source_path" : [self.CHAR_FILE, self.CHAR_UNIX, self.CHAR_DEV, self.CHAR_PIPE], "source_mode" : [self.CHAR_UNIX, self.CHAR_TCP], "source_host" : [self.CHAR_TCP, self.CHAR_UDP], "source_port" : [self.CHAR_TCP, self.CHAR_UDP], "protocol" : [self.CHAR_TCP], "bind_host" : [self.CHAR_UDP], "bind_port" : [self.CHAR_UDP], } if ro: users["source_path"] += [self.CHAR_PTY] channel_users = { "target_name" : [self.CHAR_CHANNEL_TARGET_VIRTIO], } if users.get(propname): return self.char_type in users[propname] if channel_users.get(propname): return (self.dev_type == self.DEV_CHANNEL and self.target_type in channel_users[propname]) return hasattr(self, propname) # Properties def get_dev_type(self): return self._dev_type dev_type = property(get_dev_type) def get_char_type(self): return self._char_type def set_char_type(self, val): if val not in self.char_types: raise ValueError(_("Unknown character device type '%s'") % val) self._char_type = val char_type = _xml_property(get_char_type, set_char_type, doc=_("Method used to expose character device in the host."), xpath="./@type") # Properties functions used by the various subclasses def get_source_path(self): return self._source_path def set_source_path(self, val): self._source_path = val def _sourcepath_get_xpath(self): return "./source/@path | ./@tty" source_path = _xml_property(get_source_path, set_source_path, xml_get_xpath=_sourcepath_get_xpath, xpath="./source/@path") def get_source_mode(self): return self._source_mode def set_source_mode(self, val): if val not in self.char_modes: raise ValueError(_("Unknown character mode '%s'.") % val) self._source_mode = val def _sourcemode_xpath(self): if self.char_type == self.CHAR_UDP: return "./source[@mode='connect']/@mode" return "./source/@mode" source_mode = _xml_property(get_source_mode, set_source_mode, xml_get_xpath=_sourcemode_xpath, xml_set_xpath=_sourcemode_xpath) def get_source_host(self): return self._source_host def set_source_host(self, val): self._source_host = val def _sourcehost_xpath(self): return "./source[@mode='%s']/@host" % self.source_mode source_host = _xml_property(get_source_host, set_source_host, xml_get_xpath=_sourcehost_xpath, xml_set_xpath=_sourcehost_xpath) def get_source_port(self): return self._source_port def set_source_port(self, val): self._source_port = int(val) def _sourceport_xpath(self): return "./source[@mode='%s']/@service" % self.source_mode source_port = _xml_property(get_source_port, set_source_port, xml_get_xpath=_sourceport_xpath, xml_set_xpath=_sourceport_xpath) def get_bind_host(self): return self._bind_host def set_bind_host(self, val): self._bind_host = val bind_host = _xml_property(get_bind_host, set_bind_host, xpath="./source[@mode='bind']/@host") def get_bind_port(self): return self._bind_port def set_bind_port(self, val): self._bind_port = int(val) bind_port = _xml_property(get_bind_port, set_bind_port, xpath="./source[@mode='bind']/@service") def get_protocol(self): return self._protocol def set_protocol(self, val): if val not in self.char_protocols: raise ValueError(_("Unknown protocol '%s'.") % val) self._protocol = val protocol = _xml_property(get_protocol, set_protocol, xpath="./protocol/@type") # GuestFWD target properties def get_target_type(self): return self._target_type def set_target_type(self, val): if val not in self.target_types: raise ValueError(_("Unknown target type '%s'. Must be in: ") % val, self.target_types) self._target_type = val target_type = _xml_property(get_target_type, set_target_type, doc=_("Channel type as exposed in the guest."), xpath="./target/@type") def set_target_address(self, val): self._target_address = val def get_target_address(self): return self._target_address target_address = _xml_property(get_target_address, set_target_address, doc=_("Guest forward channel address in the guest."), xpath="./target/@address") def set_target_port(self, val): self._target_port = val def get_target_port(self): return self._target_port target_port = _xml_property(get_target_port, set_target_port, doc=_("Guest forward channel port in the guest."), xpath="./target/@port") def set_target_name(self, val): self._target_name = val def get_target_name(self): return self._target_name target_name = _xml_property(get_target_name, set_target_name, doc=_("Sysfs name of virtio port in the guest"), xpath="./target/@name") def get_address_type(self): return self._address_type def set_address_type(self, val): if val not in self.address_types: raise ValueError(_("Unknown address type '%s'. Must be in: ") % val, self.address_types) self._address_type = val address_type = _xml_property(get_address_type, set_address_type, doc=_("Channel type as exposed in the guest."), xpath="./address/@type") # XML building helpers def _char_empty_xml(self): """ Provide source xml for devices with no params (null, stdio, ...) """ return "" def _char_file_xml(self): """ Provide source xml for devs that require only a path (dev, pipe) """ file_xml = "" mode_xml = "" if self.source_path: file_xml = " path='%s'" % xml_escape(self.source_path) else: raise ValueError(_("A source path is required for character " "device type '%s'" % self.char_type)) if self.supports_property("source_mode") and self.source_mode: mode_xml = " mode='%s'" % xml_escape(self.source_mode) xml = " <source%s%s/>\n" % (mode_xml, file_xml) return xml def _char_xml(self): pass def _get_target_xml(self): xml = "" if not self.target_type: return xml xml = " <target type='%s'" % self.target_type if self._dev_type == self.DEV_CHANNEL: if self.target_type == self.CHAR_CHANNEL_TARGET_GUESTFWD: if not self.target_address and not self.target_port: raise RuntimeError("A target address and port must be " "specified for '%s'" % self.target_type) xml += " address='%s'" % self.target_address xml += " port='%s'" % self.target_port elif self.target_type == self.CHAR_CHANNEL_TARGET_VIRTIO: if self.target_name: xml += " name='%s'" % self.target_name xml += "/>\n" return xml def _get_address_xml(self): xml = "" if not self.address_type: return xml xml = " <address type='%s'" % self.address_type xml += "/>\n" return xml def _get_xml_config(self): xml = " <%s type='%s'" % (self._dev_type, self._char_type) char_xml = self._char_xml() target_xml = self._get_target_xml() has_target = (self._dev_type == self.DEV_CHANNEL or self._dev_type == self.DEV_CONSOLE or self.has_target) if target_xml and not has_target: raise RuntimeError( "Target parameters not used with '%s' devices, only '%s'" % (self._dev_type, self.DEV_CHANNEL)) address_xml = self._get_address_xml() has_address = self._target_type == self.CHAR_CHANNEL_TARGET_VIRTIO if address_xml and not has_address: raise RuntimeError( "Address parameters not used with '%s' target, only '%s'" % (self._target_type, self.CHAR_CHANNEL_TARGET_VIRTIO)) if char_xml or target_xml or address_xml: xml += ">" if char_xml: xml += "\n%s" % char_xml if target_xml: xml += "\n%s" % target_xml if address_xml: xml += "\n%s" % target_xml xml += " </%s>" % self._dev_type else: xml += "/>" return xml
if newnet is not None and self.conn: try: net = self.conn.networkLookupByName(newnet) except libvirt.libvirtError, e: raise ValueError( _("Virtual network '%s' does not exist: %s") % (newnet, str(e))) if not _is_net_active(net): raise ValueError( _("Virtual network '%s' has not been " "started.") % newnet) self._network = newnet network = _xml_property(get_network, set_network, xpath="./source/@network") def get_bridge(self): if (not self._is_parse() and not self._bridge and self.type == self.TYPE_BRIDGE): return self._generate_default_bridge() return self._bridge def set_bridge(self, val): self._bridge = val bridge = _xml_property(get_bridge, set_bridge, xpath="./source/@bridge") def get_model(self): return self._model
class DomainFeatures(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <features> XML """ _dumpxml_xpath = "/domain/features" def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) self._acpi = None self._apic = None self._pae = None def get_acpi(self): return self._acpi def set_acpi(self, val): self._acpi = _none_or_bool(val) acpi = _xml_property(get_acpi, set_acpi, xpath="./features/acpi", is_bool=True) def get_apic(self): return self._apic def set_apic(self, val): self._apic = _none_or_bool(val) apic = _xml_property(get_apic, set_apic, xpath="./features/apic", is_bool=True) def get_pae(self): return self._pae def set_pae(self, val): self._pae = _none_or_bool(val) pae = _xml_property(get_pae, set_pae, xpath="./features/pae", is_bool=True) def __setitem__(self, attr, val): return setattr(self, attr, val) def __getitem__(self, attr): return getattr(self, attr) def __delitem__(self, attr): return setattr(self, attr, None) def _get_xml_config(self, defaults=None): # pylint: disable=W0221 # Argument number differs from overridden method if not defaults: defaults = {} ret = "" feature_xml = "" if self.acpi or (self.acpi is None and defaults.get("acpi")): feature_xml += "<acpi/>" if self.apic or (self.apic is None and defaults.get("apic")): feature_xml += "<apic/>" if self.pae or (self.pae is None and defaults.get("pae")): feature_xml += "<pae/>" if feature_xml: ret += " <features>\n" ret += " %s\n" % feature_xml ret += " </features>" return ret
class VirtualController(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_CONTROLLER CONTROLLER_TYPE_IDE = "ide" CONTROLLER_TYPE_FDC = "fdc" CONTROLLER_TYPE_SCSI = "scsi" CONTROLLER_TYPE_SATA = "sata" CONTROLLER_TYPE_VIRTIOSERIAL = "virtio-serial" CONTROLLER_TYPE_USB = "usb" CONTROLLER_TYPE_PCI = "pci" CONTROLLER_TYPE_CCID = "ccid" CONTROLLER_TYPES = [ CONTROLLER_TYPE_IDE, CONTROLLER_TYPE_FDC, CONTROLLER_TYPE_SCSI, CONTROLLER_TYPE_SATA, CONTROLLER_TYPE_VIRTIOSERIAL, CONTROLLER_TYPE_USB, CONTROLLER_TYPE_PCI, CONTROLLER_TYPE_CCID ] @staticmethod def pretty_type(ctype): pretty_mappings = { VirtualController.CONTROLLER_TYPE_IDE: "IDE", VirtualController.CONTROLLER_TYPE_FDC: "Floppy", VirtualController.CONTROLLER_TYPE_SCSI: "SCSI", VirtualController.CONTROLLER_TYPE_SATA: "SATA", VirtualController.CONTROLLER_TYPE_VIRTIOSERIAL: "Virtio Serial", VirtualController.CONTROLLER_TYPE_USB: "USB", VirtualController.CONTROLLER_TYPE_PCI: "PCI", VirtualController.CONTROLLER_TYPE_CCID: "CCID", } if ctype not in pretty_mappings: return ctype return pretty_mappings[ctype] @staticmethod def get_class_for_type(ctype): if ctype not in VirtualController.CONTROLLER_TYPES: raise ValueError("Unknown controller type '%s'" % ctype) if ctype == VirtualController.CONTROLLER_TYPE_IDE: return VirtualControllerIDE elif ctype == VirtualController.CONTROLLER_TYPE_FDC: return VirtualControllerFDC elif ctype == VirtualController.CONTROLLER_TYPE_SCSI: return VirtualControllerSCSI elif ctype == VirtualController.CONTROLLER_TYPE_SATA: return VirtualControllerSATA elif ctype == VirtualController.CONTROLLER_TYPE_VIRTIOSERIAL: return VirtualControllerVirtioSerial elif ctype == VirtualController.CONTROLLER_TYPE_USB: return VirtualControllerUSB _controller_type = None def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None, model=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._index = 0 self._ports = None self._vectors = None self._model = None self._master = VirtualDeviceMaster(conn, parsexml=parsexml, parsexmlnode=parsexmlnode, caps=caps) if self._is_parse(): return self.model = model def get_type(self): return self._controller_type type = _xml_property(get_type, xpath="./@type") def get_model(self): return self._model def set_model(self, model): self._model = model model = _xml_property(get_model, set_model, xpath="./@model") def get_index(self): return self._index def set_index(self, val): self._index = int(val) index = _xml_property(get_index, set_index, xpath="./@index") def get_vectors(self): return self._vectors def set_vectors(self, val): self._vectors = val vectors = _xml_property(get_vectors, set_vectors, xpath="./@vectors") def get_ports(self): return self._ports def set_ports(self, val): self._ports = val ports = _xml_property(get_ports, set_ports, xpath="./@ports") def set_master(self, masterstr): self._master.parse_friendly_master(masterstr) def get_master(self): return self._master def _extra_config(self): return "" def _get_xml_config(self): extra = self._extra_config() xml = " <controller type='%s' index='%s'" % (self.type, self.index) if self.model: xml += " model='%s'" % self.model xml += extra childxml = self.indent(self._master.get_xml_config(), 6) childxml += self.indent(self.address.get_xml_config(), 6) if len(childxml) == 0: return xml + "/>" xml += ">\n" xml += childxml xml += " </controller>" return xml
class VirtualHostDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_HOSTDEV def device_from_node(conn, name=None, nodedev=None, is_dup=False): """ Convert the passed device name to a VirtualHostDevice instance, with proper error reporting. Name can be any of the values accepted by NodeDeviceParser.lookupNodeName. If a node device name is not specified, a virtinst.NodeDevice instance can be passed in to create a dev from. @param conn: libvirt.virConnect instance to perform the lookup on @param name: optional libvirt node device name to lookup @param nodedev: optional L{virtinst.NodeDevice} instance to use @rtype: L{virtinst.VirtualHostDevice} instance """ if not name and not nodedev: raise ValueError(_("'name' or 'nodedev' required.")) if nodedev: nodeinst = nodedev else: nodeinst, addr_type = NodeDeviceParser.lookupNodeName(conn, name) if addr_type == NodeDeviceParser.HOSTDEV_ADDR_TYPE_USB_BUSADDR: is_dup = True if isinstance(nodeinst, NodeDeviceParser.PCIDevice): return VirtualHostDevicePCI(conn, nodedev=nodeinst) elif isinstance(nodeinst, NodeDeviceParser.USBDevice): return VirtualHostDeviceUSB(conn, nodedev=nodeinst, is_dup=is_dup) elif isinstance(nodeinst, NodeDeviceParser.NetDevice): parentname = nodeinst.parent try: return VirtualHostDevice.device_from_node(conn, name=parentname) except: logging.exception("Fetching net parent device failed.") raise ValueError( _("Node device type '%s' cannot be attached to " " guest.") % nodeinst.device_type) device_from_node = staticmethod(device_from_node) def __init__(self, conn, nodedev=None, parsexml=None, parsexmlnode=None, caps=None): """ @param conn: Connection the device/guest will be installed on @type conn: libvirt.virConnect @param nodedev: Optional NodeDevice instance for device being attached to the guest @type nodedev: L{virtinst.NodeDeviceParser.NodeDevice} """ VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._mode = None self._type = None self._managed = None self._nodedev = nodedev self._vendor = None self._product = None self._bus = None self._device = None self._domain = "0x0" self._slot = None self._function = None if self._is_parse(): return self.managed = True if self.is_xen(): self.managed = False def get_mode(self): return self._mode def set_mode(self, val): self._mode = val mode = _xml_property(get_mode, set_mode, xpath="./@mode") def get_type(self): return self._type def set_type(self, val): self._type = val type = _xml_property(get_type, set_type, xpath="./@type") def get_managed(self): return self._managed def set_managed(self, val): self._managed = bool(val) managed = _xml_property(get_managed, set_managed, get_converter=lambda s, x: bool(x == "yes"), set_converter=lambda s, x: x and "yes" or "no", xpath="./@managed") def get_vendor(self): return self._vendor def set_vendor(self, val): self._vendor = val vendor = _xml_property(get_vendor, set_vendor, xpath="./source/vendor/@id") def get_product(self): return self._product def set_product(self, val): self._product = val product = _xml_property(get_product, set_product, xpath="./source/product/@id") def get_device(self): return self._device def set_device(self, val): self._device = val device = _xml_property(get_device, set_device, xpath="./source/address/@device") def get_bus(self): return self._bus def set_bus(self, val): self._bus = val bus = _xml_property(get_bus, set_bus, xpath="./source/address/@bus") def get_function(self): return self._function def set_function(self, val): self._function = val function = _xml_property(get_function, set_function, xpath="./source/address/@function") def get_domain(self): return self._domain def set_domain(self, val): self._domain = val domain = _xml_property(get_domain, set_domain, xpath="./source/address/@domain") def get_slot(self): return self._slot def set_slot(self, val): self._slot = val slot = _xml_property(get_slot, set_slot, xpath="./source/address/@slot") def _get_source_xml(self): raise NotImplementedError("Must be implemented in subclass") def setup(self, conn=None): """ Unused @param conn: libvirt virConnect instance to use (defaults to devices connection) """ ignore = conn def _get_xml_config(self): xml = (" <hostdev mode='%s' type='%s' managed='%s'>\n" % (self.mode, self.type, self.managed and "yes" or "no")) xml += " <source>\n" xml += self._get_source_xml() xml += " </source>\n" xml += " </hostdev>" return xml
class VirtualFilesystem(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_FILESYSTEM _target_props = ["dir", "name", "file", "dev"] TYPE_MOUNT = "mount" TYPE_TEMPLATE = "template" TYPE_FILE = "file" TYPE_BLOCK = "block" TYPE_DEFAULT = "default" TYPES = [TYPE_MOUNT, TYPE_TEMPLATE, TYPE_FILE, TYPE_BLOCK, TYPE_DEFAULT] MODE_PASSTHROUGH = "passthrough" MODE_MAPPED = "mapped" MODE_SQUASH = "squash" MODE_DEFAULT = "default" MOUNT_MODES = [MODE_PASSTHROUGH, MODE_MAPPED, MODE_SQUASH, MODE_DEFAULT] WRPOLICY_IMM = "immediate" WRPOLICY_DEFAULT = "default" WRPOLICIES = [WRPOLICY_IMM, WRPOLICY_DEFAULT] DRIVER_PATH = "path" DRIVER_HANDLE = "handle" DRIVER_PROXY = "proxy" DRIVER_DEFAULT = "default" DRIVER_TYPES = [DRIVER_PATH, DRIVER_HANDLE, DRIVER_PROXY, DRIVER_DEFAULT] @staticmethod def type_to_source_prop(fs_type): """ Convert a value of VirtualFilesystem.type to it's associated XML source @prop name """ if (fs_type == VirtualFilesystem.TYPE_MOUNT or fs_type == VirtualFilesystem.TYPE_DEFAULT or fs_type is None): return "dir" elif fs_type == VirtualFilesystem.TYPE_TEMPLATE: return "name" elif fs_type == VirtualFilesystem.TYPE_FILE: return "file" elif fs_type == VirtualFilesystem.TYPE_BLOCK: return "dev" return "dir" def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._mode = None self._driver = None self._target = None self._source = None self._readonly = None self._wrpolicy = None if self._is_parse(): return self.mode = self.MODE_DEFAULT self.type = self.TYPE_DEFAULT self.driver = self.DRIVER_DEFAULT self.wrpolicy = self.WRPOLICY_DEFAULT def _get_type(self): return self._type def _set_type(self, val): if val is not None and not self.TYPES.count(val): raise ValueError(_("Unsupported filesystem type '%s'" % val)) self._type = val type = _xml_property(_get_type, _set_type, xpath="./@type") def _get_mode(self): return self._mode def _set_mode(self, val): if val is not None and not self.MOUNT_MODES.count(val): raise ValueError(_("Unsupported filesystem mode '%s'" % val)) self._mode = val mode = _xml_property(_get_mode, _set_mode, xpath="./@accessmode") def _get_wrpolicy(self): return self._wrpolicy def _set_wrpolicy(self, val): if val is not None and not self.WRPOLICIES.count(val): raise ValueError( _("Unsupported filesystem write policy '%s'" % val)) self._wrpolicy = val wrpolicy = _xml_property(_get_wrpolicy, _set_wrpolicy, xpath="./driver/@wrpolicy") def _get_readonly(self): return self._readonly def _set_readonly(self, val): self._readonly = val readonly = _xml_property(_get_readonly, _set_readonly, xpath="./readonly", is_bool=True) def _get_driver(self): return self._driver def _set_driver(self, val): if val is not None and not self.DRIVER_TYPES.count(val): raise ValueError(_("Unsupported filesystem driver '%s'" % val)) self._driver = val driver = _xml_property(_get_driver, _set_driver, xpath="./driver/@type") def _get_source(self): return self._source def _set_source(self, val): if self.type != self.TYPE_TEMPLATE: val = os.path.abspath(val) self._source = val def _xml_get_source_xpath(self): xpath = None ret = "./source/@dir" for prop in self._target_props: xpath = "./source/@" + prop if self._xml_ctx.xpathEval(xpath): ret = xpath return ret def _xml_set_source_xpath(self): ret = "./source/@" + self.type_to_source_prop(self.type) return ret source = _xml_property(_get_source, _set_source, xml_get_xpath=_xml_get_source_xpath, xml_set_xpath=_xml_set_source_xpath) def _get_target(self): return self._target def _set_target(self, val): is_qemu = self.is_qemu() # In case of qemu for default fs type (mount) target is not # actually a directory, it is merely a arbitrary string tag # that is exported to the guest as a hint for where to mount if (is_qemu and (self.type == self.TYPE_DEFAULT or self.type == self.TYPE_MOUNT)): pass elif not os.path.isabs(val): raise ValueError( _("Filesystem target '%s' must be an absolute " "path") % val) self._target = val target = _xml_property(_get_target, _set_target, xpath="./target/@dir") def _get_xml_config(self): mode = self.mode ftype = self.type driver = self.driver source = self.source target = self.target readonly = self.readonly wrpolicy = self.wrpolicy if mode == self.MODE_DEFAULT: mode = None if ftype == self.TYPE_DEFAULT: ftype = None if driver == self.DRIVER_DEFAULT: driver = None wrpolicy = None if wrpolicy == self.WRPOLICY_DEFAULT: wrpolicy = None if not source or not target: raise ValueError( _("A filesystem source and target must be specified")) fsxml = " <filesystem" if ftype: fsxml += " type='%s'" % ftype if mode: fsxml += " accessmode='%s'" % mode fsxml += ">\n" if driver: if not wrpolicy: fsxml += " <driver type='%s'/>\n" % driver else: fsxml += " <driver type='%s' wrpolicy='%s' />\n" % ( driver, wrpolicy) fsxml += " <source %s='%s'/>\n" % ( self.type_to_source_prop(ftype), source) fsxml += " <target dir='%s'/>\n" % target if readonly: fsxml += " <readonly/>\n" fsxml += " </filesystem>" return fsxml
class VirtualVideoDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_VIDEO # Default models list MODEL_DEFAULT = "default" _model_types = ["cirrus", "vga", "vmvga", "xen", "qxl", MODEL_DEFAULT] @staticmethod def pretty_model(model): if model in ["qxl", "vmvga"]: return model.upper() return model.capitalize() def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._model_type = None self._vram = None self._heads = None if self._is_parse(): return self.model_type = self.MODEL_DEFAULT def get_model_types(self): return self._model_types[:] model_types = property(get_model_types) def get_model_type(self): return self._model_type def set_model_type(self, val): self._model_type = val model_type = _xml_property(get_model_type, set_model_type, xpath="./model/@type") def get_vram(self): return self._vram def set_vram(self, val): self._vram = val vram = _xml_property(get_vram, set_vram, xpath="./model/@vram") ram = _xml_property(lambda o: None, lambda o, v: None, xpath="./model/@ram") def get_heads(self): return self._heads def set_heads(self, val): self._heads = val heads = _xml_property(get_heads, set_heads, xpath="./model/@heads") def _get_xml_config(self): model = self.model_type if self.model_type == self.MODEL_DEFAULT: model = "cirrus" model_xml = " <model" if self.model_type: model_xml += " type='%s'" % model if self.vram: model_xml += " vram='%s'" % self.vram if self.heads: model_xml += " heads='%s'" % self.heads model_xml += "/>\n" xml = (" <video>\n" + model_xml + " </video>") return xml
class Seclabel(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating <seclabel> XML """ SECLABEL_TYPE_DYNAMIC = "dynamic" SECLABEL_TYPE_STATIC = "static" SECLABEL_TYPE_DEFAULT = "default" SECLABEL_TYPES = [SECLABEL_TYPE_DYNAMIC, SECLABEL_TYPE_STATIC] MODEL_DEFAULT = "default" SECLABEL_MODEL_TEST = "testSecurity" SECLABEL_MODEL_SELINUX = "selinux" SECLABEL_MODEL_DAC = "dac" SECLABEL_MODEL_NONE = "none" SECLABEL_MODELS = [ SECLABEL_MODEL_SELINUX, SECLABEL_MODEL_DAC, SECLABEL_MODEL_NONE ] _dumpxml_xpath = "/domain/seclabel" def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._model = None self._label = None self._imagelabel = None self._relabel = None if self._is_parse(): return self.model = self.MODEL_DEFAULT self.type = self.SECLABEL_TYPE_DEFAULT def _get_default_model(self): caps = self._get_caps() if caps: if (self.SECLABEL_MODEL_TEST in [x.model for x in caps.host.secmodels]): return self.SECLABEL_MODEL_TEST for model in self.SECLABEL_MODELS: if model in [x.model for x in caps.host.secmodels]: return model raise RuntimeError("No supported model found in capabilities") def _guess_secmodel(self, label, imagelabel): # We always want the testSecurity model when running tests caps = self._get_caps() if (caps and self.SECLABEL_MODEL_TEST in [x.model for x in caps.host.secmodels]): return self.SECLABEL_MODEL_TEST if not label and not imagelabel: return self._get_default_model() lab_len = imglab_len = None if label: lab_len = min(3, len(label.split(':'))) if imagelabel: imglab_len = min(3, len(imagelabel.split(':'))) if lab_len and imglab_len and lab_len != imglab_len: raise ValueError("Label and Imagelabel are incompatible") lab_len = lab_len or imglab_len if lab_len == 3: return self.SECLABEL_MODEL_SELINUX elif lab_len == 2: return self.SECLABEL_MODEL_DAC else: raise ValueError("Unknown model type for label '%s'" % self.label) def get_type(self): return self._type def set_type(self, val): if (val not in self.SECLABEL_TYPES and val != self.SECLABEL_TYPE_DEFAULT): raise ValueError("Unknown security type '%s'" % val) self._type = val type = _xml_property(get_type, set_type, xpath="./seclabel/@type") def get_model(self): return self._model def set_model(self, val): self._model = val model = _xml_property(get_model, set_model, xpath="./seclabel/@model", default_converter=_get_default_model) def get_label(self): return self._label def set_label(self, val): self._label = val label = _xml_property(get_label, set_label, xpath="./seclabel/label") def _get_relabel(self): return self._relabel def _set_relabel(self, val): self._relabel = val relabel = _xml_property(_get_relabel, _set_relabel, xpath="./seclabel/@relabel") def get_imagelabel(self): return self._imagelabel def set_imagelabel(self, val): self._imagelabel = val imagelabel = _xml_property(get_imagelabel, set_imagelabel, xpath="./seclabel/imagelabel") def _get_xml_config(self): if (self.model == self.MODEL_DEFAULT and self.type == self.SECLABEL_TYPE_DEFAULT): return "" model = self.model typ = self.type relabel = self.relabel if typ == self.SECLABEL_TYPE_DEFAULT: typ = self.SECLABEL_TYPE_DYNAMIC if not typ: raise RuntimeError("Security type and model must be specified") if typ == self.SECLABEL_TYPE_STATIC: if not self.label: raise RuntimeError("A label must be specified for static " "security type.") if model == self.MODEL_DEFAULT: model = self._guess_secmodel(self.label, self.imagelabel) label_xml = "" xml = " <seclabel type='%s' model='%s'" % (typ, model) if relabel is not None: xml += " relabel='%s'" % (relabel and "yes" or "no") if self.label: label_xml += " <label>%s</label>\n" % self.label if self.imagelabel: label_xml += " <imagelabel>%s</imagelabel>\n" % self.imagelabel if label_xml: xml += ">\n" xml += label_xml xml += " </seclabel>" else: xml += "/>" return xml
class VirtualNetworkInterface(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_NET TYPE_BRIDGE = "bridge" TYPE_VIRTUAL = "network" TYPE_USER = "******" TYPE_ETHERNET = "ethernet" TYPE_DIRECT = "direct" network_types = [ TYPE_BRIDGE, TYPE_VIRTUAL, TYPE_USER, TYPE_ETHERNET, TYPE_DIRECT ] def get_network_type_desc(net_type): """ Return human readable description for passed network type """ desc = net_type.capitalize() if net_type == VirtualNetworkInterface.TYPE_BRIDGE: desc = _("Shared physical device") elif net_type == VirtualNetworkInterface.TYPE_VIRTUAL: desc = _("Virtual networking") elif net_type == VirtualNetworkInterface.TYPE_USER: desc = _("Usermode networking") return desc get_network_type_desc = staticmethod(get_network_type_desc) def __init__(self, macaddr=None, type=TYPE_BRIDGE, bridge=None, network=None, model=None, conn=None, parsexml=None, parsexmlnode=None, caps=None): # pylint: disable=W0622 # Redefining built-in 'type', but it matches the XML so keep it VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._network = None self._bridge = None self._macaddr = None self._type = None self._model = None self._target_dev = None self._source_dev = None self._source_mode = "vepa" self._virtualport = VirtualPort(conn, parsexml, parsexmlnode, caps) # Generate _random_mac self._random_mac = None self._default_bridge = None if self._is_parse(): return self.type = type self.macaddr = macaddr self.bridge = bridge self.source_dev = bridge self.network = network self.model = model if self.type == self.TYPE_VIRTUAL: if network is None: raise ValueError(_("A network name was not provided")) def _generate_default_bridge(self): ret = self._default_bridge if ret is None: ret = False default = util.default_bridge(self.conn) if default: ret = default[1] self._default_bridge = ret return ret or None def _generate_random_mac(self): if self.conn and not self._random_mac: found = False for ignore in range(256): self._random_mac = util.randomMAC(self.conn.getType().lower(), conn=self.conn) ret = self.is_conflict_net(self.conn, self._random_mac) if ret[1] is not None: continue found = True break if not found: logging.debug("Failed to generate non-conflicting MAC") return self._random_mac def get_source(self): """ Convenince function, try to return the relevant <source> value per the network type. """ if self.type == self.TYPE_VIRTUAL: return self.network if self.type == self.TYPE_BRIDGE: return self.bridge if self.type == self.TYPE_ETHERNET or self.type == self.TYPE_DIRECT: return self.source_dev if self.type == self.TYPE_USER: return None return self.network or self.bridge or self.source_dev def set_source(self, newsource): """ Conveninece function, try to set the relevant <source> value per the network type """ if self.type == self.TYPE_VIRTUAL: self.network = newsource elif self.type == self.TYPE_BRIDGE: self.bridge = newsource elif self.type == self.TYPE_ETHERNET or self.type == self.TYPE_DIRECT: self.source_dev = newsource return source = property(get_source, set_source) def _get_virtualport(self): return self._virtualport virtualport = property(_get_virtualport) def get_type(self): return self._type def set_type(self, val): if val not in self.network_types: raise ValueError(_("Unknown network type %s") % val) self._type = val type = _xml_property(get_type, set_type, xpath="./@type") def get_macaddr(self): # Don't generate a random MAC if parsing XML, since it can be slow if not self._macaddr and not self._is_parse(): return self._generate_random_mac() return self._macaddr def set_macaddr(self, val): util.validate_macaddr(val) self._macaddr = val macaddr = _xml_property(get_macaddr, set_macaddr, xpath="./mac/@address") def get_network(self): return self._network def set_network(self, newnet): def _is_net_active(netobj): # Apparently the 'info' command was never hooked up for # libvirt virNetwork python apis. if not self.conn: return True return self.conn.listNetworks().count(netobj.name()) if newnet is not None and self.conn: try: net = self.conn.networkLookupByName(newnet) except libvirt.libvirtError, e: raise ValueError( _("Virtual network '%s' does not exist: %s") % (newnet, str(e))) if not _is_net_active(net): raise ValueError( _("Virtual network '%s' has not been " "started.") % newnet) self._network = newnet
class Boot(XMLBuilderDomain.XMLBuilderDomain): """ Class for generating boot device related XML """ BOOT_DEVICE_HARDDISK = "hd" BOOT_DEVICE_CDROM = "cdrom" BOOT_DEVICE_FLOPPY = "fd" BOOT_DEVICE_NETWORK = "network" boot_devices = [ BOOT_DEVICE_HARDDISK, BOOT_DEVICE_CDROM, BOOT_DEVICE_FLOPPY, BOOT_DEVICE_NETWORK ] _dumpxml_xpath = "/domain/os" def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps) self._bootorder = [] self._enable_bootmenu = None self._kernel = None self._initrd = None self._kernel_args = None def _get_enable_bootmenu(self): return self._enable_bootmenu def _set_enable_bootmenu(self, val): self._enable_bootmenu = val def _get_menu_converter(self, val): ignore = self if val is None: return None return bool(val == "yes") enable_bootmenu = _xml_property( _get_enable_bootmenu, _set_enable_bootmenu, get_converter=_get_menu_converter, set_converter=lambda s, x: x and "yes" or "no", xpath="./os/bootmenu/@enable") def _get_bootorder(self): return self._bootorder def _set_bootorder(self, val): self._bootorder = val def _bootorder_xpath_list(self): l = [] for idx in range(len(self._get_bootorder())): l.append("./os/boot[%d]/@dev" % (idx + 1)) return l bootorder = _xml_property(_get_bootorder, _set_bootorder, is_multi=True, xml_set_list=_bootorder_xpath_list, xpath="./os/boot/@dev") def _get_kernel(self): return self._kernel def _set_kernel(self, val): self._kernel = val kernel = _xml_property(_get_kernel, _set_kernel, xpath="./os/kernel") def _get_initrd(self): return self._initrd def _set_initrd(self, val): self._initrd = val initrd = _xml_property(_get_initrd, _set_initrd, xpath="./os/initrd") def _get_kernel_args(self): return self._kernel_args def _set_kernel_args(self, val): self._kernel_args = val kernel_args = _xml_property(_get_kernel_args, _set_kernel_args, xpath="./os/cmdline") def _get_xml_config(self): xml = "" if self.kernel: xml = util.xml_append( xml, " <kernel>%s</kernel>" % util.xml_escape(self.kernel)) if self.initrd: xml = util.xml_append( xml, " <initrd>%s</initrd>" % util.xml_escape(self.initrd)) if self.kernel_args: xml = util.xml_append( xml, " <cmdline>%s</cmdline>" % util.xml_escape(self.kernel_args)) else: for dev in self.bootorder: xml = util.xml_append(xml, " <boot dev='%s'/>" % dev) if self.enable_bootmenu in [True, False]: val = self.enable_bootmenu and "yes" or "no" xml = util.xml_append(xml, " <bootmenu enable='%s'/>" % val) return xml
class VirtualPort(XMLBuilderDomain.XMLBuilderDomain): def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) self._type = None self._managerid = None self._typeid = None self._typeidversion = None self._instanceid = None def get_type(self): return self._type def set_type(self, val): self._type = val type = _xml_property(get_type, set_type, xpath="./virtualport/@type") def get_managerid(self): return self._managerid def set_managerid(self, val): self._managerid = val managerid = _xml_property(get_managerid, set_managerid, xpath="./virtualport/parameters/@managerid") def get_typeid(self): return self._typeid def set_typeid(self, val): self._typeid = val typeid = _xml_property(get_typeid, set_typeid, xpath="./virtualport/parameters/@typeid") def get_typeidversion(self): return self._typeidversion def set_typeidversion(self, val): self._typeidversion = val typeidversion = _xml_property( get_typeidversion, set_typeidversion, xpath="./virtualport/parameters/@typeidversion") def get_instanceid(self): return self._instanceid def set_instanceid(self, val): self._instanceid = val instanceid = _xml_property(get_instanceid, set_instanceid, xpath="./virtualport/parameters/@instanceid") def _get_xml_config(self): # FIXME: This should be implemented, currently we can only parse return ""
if not self.conn: return True return self.conn.listNetworks().count(netobj.name()) if newnet is not None and self.conn: try: net = self.conn.networkLookupByName(newnet) except libvirt.libvirtError, e: raise ValueError(_("Virtual network '%s' does not exist: %s") % (newnet, str(e))) if not _is_net_active(net): raise ValueError(_("Virtual network '%s' has not been " "started.") % newnet) self._network = newnet network = _xml_property(get_network, set_network, xpath="./source/@network") def get_bridge(self): if (not self._is_parse() and not self._bridge and self.type == self.TYPE_BRIDGE): return self._generate_default_bridge() return self._bridge def set_bridge(self, val): self._bridge = val bridge = _xml_property(get_bridge, set_bridge, xpath="./source/@bridge") def get_model(self): return self._model def set_model(self, val):
class VirtualInputDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_INPUT INPUT_TYPE_MOUSE = "mouse" INPUT_TYPE_TABLET = "tablet" INPUT_TYPE_DEFAULT = "default" input_types = [INPUT_TYPE_MOUSE, INPUT_TYPE_TABLET, INPUT_TYPE_DEFAULT] INPUT_BUS_PS2 = "ps2" INPUT_BUS_USB = "usb" INPUT_BUS_XEN = "xen" INPUT_BUS_DEFAULT = "default" input_buses = [ INPUT_BUS_PS2, INPUT_BUS_USB, INPUT_BUS_XEN, INPUT_BUS_DEFAULT ] def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._bus = None if self._is_parse(): return self.type = self.INPUT_TYPE_DEFAULT self.bus = self.INPUT_BUS_DEFAULT def _convert_default_bus(self, val): if val == self.INPUT_BUS_DEFAULT: return self.INPUT_BUS_XEN return val def _convert_default_type(self, val): if val == self.INPUT_TYPE_DEFAULT: return self.INPUT_TYPE_MOUSE return val def get_type(self): return self._type def set_type(self, val): if val not in self.input_types: raise ValueError(_("Unknown input type '%s'.") % val) self._type = val type = _xml_property(get_type, set_type, xpath="./@type") def get_bus(self): return self._bus def set_bus(self, val): if val not in self.input_buses: raise ValueError(_("Unknown input bus '%s'.") % val) self._bus = val bus = _xml_property(get_bus, set_bus, xpath="./@bus") def _get_xml_config(self): typ = self._convert_default_type(self.type) bus = self._convert_default_bus(self.bus) return " <input type='%s' bus='%s'/>" % (typ, bus)
class VirtualDeviceAddress(XMLBuilderDomain): ADDRESS_TYPE_PCI = "pci" ADDRESS_TYPE_DRIVE = "drive" ADDRESS_TYPE_VIRTIO_SERIAL = "virtio-serial" ADDRESS_TYPE_CCID = "ccid" ADDRESS_TYPE_SPAPR_VIO = "spapr-vio" TYPES = [ ADDRESS_TYPE_PCI, ADDRESS_TYPE_DRIVE, ADDRESS_TYPE_VIRTIO_SERIAL, ADDRESS_TYPE_CCID, ADDRESS_TYPE_SPAPR_VIO ] def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None, addrstr=None): XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) self._type = None # PCI address: # <address type='pci' domain='0x0000' bus='0x00' slot='0x04' \ # function='0x0'/> self._bus = None self._domain = None self._slot = None self._function = None self._multifunction = None # Drive address: # <address type='drive' controller='0' bus='0' target='0' unit='0'/> self._controller = None self._target = None self._unit = None # VirtioSerial address: # <address type='virtio-serial' controller='1' bus='0' port='4'/> self._port = None # CCID address: # <address type='ccid' controller='0' slot='0'/> if addrstr: self.parse_friendly_address(addrstr) def parse_friendly_address(self, addrstr): try: if addrstr.count(":") in [1, 2] and addrstr.count("."): self.type = self.ADDRESS_TYPE_PCI addrstr, self.function = addrstr.split(".", 1) addrstr, self.slot = addrstr.rsplit(":", 1) self.domain = "0" if addrstr.count(":"): self.domain, self.bus = addrstr.split(":", 1) elif addrstr == "spapr-vio": self.type = self.ADDRESS_TYPE_SPAPR_VIO else: raise ValueError( _("Could not determine or unsupported format of '%s'") % addrstr) except: logging.exception("Error parsing address.") return None def clear(self): self._type = None self._bus = None self._domain = None self._slot = None self._function = None self._controller = None self._target = None self._unit = None self._port = None self._multifunction = None if self._is_parse(): self._remove_child_xpath("./address") def _get_type(self): return self._type def _set_type(self, val): self._type = val type = _xml_property(_get_type, _set_type, xpath="./address/@type") def _get_domain(self): return self._domain def _set_domain(self, val): self._domain = val domain = _xml_property(_get_domain, _set_domain, xpath="./address/@domain") def _get_bus(self): return self._bus def _set_bus(self, val): self._bus = val bus = _xml_property(_get_bus, _set_bus, xpath="./address/@bus") def _get_slot(self): return self._slot def _set_slot(self, val): self._slot = val slot = _xml_property(_get_slot, _set_slot, xpath="./address/@slot") def _get_function(self): return self._function def _set_function(self, val): self._function = val function = _xml_property(_get_function, _set_function, xpath="./address/@function") def _get_controller(self): return self._controller def _set_controller(self, val): self._controller = val controller = _xml_property(_get_controller, _set_controller, xpath="./address/@controller") def _get_target(self): return self._target def _set_target(self, val): self._target = val target = _xml_property(_get_target, _set_target, xpath="./address/@target") def _get_unit(self): return self._unit def _set_unit(self, val): self._unit = val unit = _xml_property(_get_unit, _set_unit, xpath="./address/@unit") def _get_port(self): return self._port def _set_port(self, val): self._port = val port = _xml_property(_get_port, _set_port, xpath="./address/@port") def _get_multifunction(self): return self._multifunction def _set_multifunction(self, val): self._multifunction = val multifunction = _xml_property(_get_multifunction, _set_multifunction, xpath="./address/@multifunction") def _get_xml_config(self): if not self.type: return def format_props(*args): return "".join([ " %s='%s'" % (k, getattr(self, k)) for k in args if getattr(self, k, None) is not None ]) xml = "<address type='%s'" % self.type if self.type == self.ADDRESS_TYPE_PCI: xml += format_props("domain", "bus", "slot", "function", "multifunction") elif self.type == self.ADDRESS_TYPE_DRIVE: xml += format_props("controller", "bus", "target", "unit") elif self.type == self.ADDRESS_TYPE_VIRTIO_SERIAL: xml += format_props("controller", "bus", "port") elif self.type == self.ADDRESS_TYPE_CCID: xml += format_props("controller", "slot") xml += "/>" return xml
class VirtualRedirDevice(VirtualDevice): _virtual_device_type = VirtualDevice.VIRTUAL_DEV_REDIRDEV BUS_DEFAULT = "usb" _buses = ["usb"] TYPE_DEFAULT = "spicevmc" _types = ["tcp", "spicevmc", None] def __init__(self, bus=BUS_DEFAULT, stype=TYPE_DEFAULT, conn=None, parsexml=None, parsexmlnode=None, caps=None): """ @param conn: Connection the device/guest will be installed on @type conn: libvirt.virConnect """ VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._type = None self._bus = None self._host = None self._service = None if self._is_parse(): return self.bus = bus self.type = stype def get_buses(self): return self._buses[:] buses = property(get_buses) def get_bus(self): return self._bus def set_bus(self, new_val): if new_val not in self.buses: raise ValueError(_("Unsupported bus '%s'" % new_val)) self._bus = new_val bus = _xml_property(get_bus, set_bus, xpath="./@bus") def get_types(self): return self._types[:] types = property(get_types) def get_type(self): return self._type def set_type(self, new_val): if new_val not in self.types: raise ValueError(_("Unsupported redirection type '%s'" % new_val)) self._type = new_val type = _xml_property(get_type, set_type, xpath="./@type") def get_host(self): return self._host def set_host(self, val): if len(val) == 0: raise ValueError(_("Invalid host value")) self._host = val host = _xml_property(get_host, set_host, xpath="./source/@host") def get_service(self): return self._service def set_service(self, val): int(val) self._service = val service = _xml_property(get_service, set_service, xpath="./source/@service") def parse_friendly_server(self, serverstr): if serverstr.count(":") == 1: self.host, self.service = serverstr.split(":") else: raise ValueError(_("Could not determine or unsupported format of '%s'") % serverstr) def _get_xml_config(self): xml = (" <redirdev bus='%s' type='%s'" % (self.bus, self.type)) if self.type == 'spicevmc': xml += "/>" return xml xml += ">\n" xml += (" <source mode='connect' host='%s' service='%s'/>\n" % (self.host, self.service)) xml += " </redirdev>" return xml