virt-manager/virtManager/domain.py

1951 lines
64 KiB
Python

#
# Copyright (C) 2006, 2013, 2014 Red Hat, Inc.
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
#
# 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.
#
# pylint: disable=E0611
from gi.repository import GObject
# pylint: enable=E0611
import logging
import os
import time
import threading
import libvirt
from virtinst import DomainSnapshot
from virtinst import Guest
from virtinst import util
from virtinst import VirtualController
from virtManager.libvirtobject import vmmLibvirtObject
vm_status_icons = {
libvirt.VIR_DOMAIN_BLOCKED: "state_running",
libvirt.VIR_DOMAIN_CRASHED: "state_shutoff",
libvirt.VIR_DOMAIN_PAUSED: "state_paused",
libvirt.VIR_DOMAIN_RUNNING: "state_running",
libvirt.VIR_DOMAIN_SHUTDOWN: "state_shutoff",
libvirt.VIR_DOMAIN_SHUTOFF: "state_shutoff",
libvirt.VIR_DOMAIN_NOSTATE: "state_running",
getattr(libvirt, "VIR_DOMAIN_PMSUSPENDED", 7): "state_paused",
}
def compare_device(origdev, newdev, idx):
devprops = {
"disk" : ["target", "bus"],
"interface" : ["macaddr", "vmmindex"],
"input" : ["bus", "type", "vmmindex"],
"sound" : ["model", "vmmindex"],
"video" : ["model", "vmmindex"],
"watchdog" : ["vmmindex"],
"hostdev" : ["type", "managed", "vmmindex",
"product", "vendor",
"function", "domain", "slot"],
"serial" : ["type", "target_port"],
"parallel" : ["type", "target_port"],
"console" : ["type", "target_type", "target_port"],
"graphics" : ["type", "vmmindex"],
"controller" : ["type", "index"],
"channel" : ["type", "target_name"],
"filesystem" : ["target" , "vmmindex"],
"smartcard" : ["mode" , "vmmindex"],
"redirdev" : ["bus" , "type", "vmmindex"],
"tpm" : ["type" , "vmmindex"],
"rng" : ["type" , "vmmindex"],
"panic" : ["type" , "vmmindex"],
}
if id(origdev) == id(newdev):
return True
if type(origdev) is not type(newdev):
return False
for devprop in devprops[origdev.virtual_device_type]:
origval = getattr(origdev, devprop)
if devprop == "vmmindex":
newval = idx
else:
newval = getattr(newdev, devprop)
if origval != newval:
return False
return True
def _find_device(guest, origdev):
devlist = guest.get_devices(origdev.virtual_device_type)
for idx in range(len(devlist)):
dev = devlist[idx]
if compare_device(origdev, dev, idx):
return dev
return None
def start_job_progress_thread(vm, meter, progtext):
current_thread = threading.currentThread()
def jobinfo_cb():
while True:
time.sleep(.5)
if not current_thread.isAlive():
return False
try:
jobinfo = vm.job_info()
data_total = float(jobinfo[3])
# data_processed = float(jobinfo[4])
data_remaining = float(jobinfo[5])
# data_total is 0 if the job hasn't started yet
if not data_total:
continue
if not meter.started:
meter.start(size=data_total,
text=progtext)
progress = data_total - data_remaining
meter.update(progress)
except:
logging.exception("Error calling jobinfo")
return False
return True
if vm.getjobinfo_supported:
t = threading.Thread(target=jobinfo_cb,
name="job progress reporting",
args=())
t.daemon = True
t.start()
class vmmInspectionData(object):
def __init__(self):
self.type = None
self.distro = None
self.major_version = None
self.minor_version = None
self.hostname = None
self.product_name = None
self.product_variant = None
self.icon = None
self.applications = None
self.error = False
class vmmDomainSnapshot(vmmLibvirtObject):
"""
Class wrapping a virDomainSnapshot object
"""
def __init__(self, conn, backend):
vmmLibvirtObject.__init__(self, conn, backend, backend.getName(),
DomainSnapshot)
self.refresh_xml()
def get_name(self):
return self.get_xmlobj().name
def _XMLDesc(self, flags):
return self._backend.getXMLDesc(flags=flags)
def delete(self, force=True):
ignore = force
self._backend.delete()
def run_status(self):
status = DomainSnapshot.state_str_to_int(self.get_xmlobj().state)
return vmmDomain.pretty_run_status(status)
def run_status_icon_name(self):
status = DomainSnapshot.state_str_to_int(self.get_xmlobj().state)
if status not in vm_status_icons:
logging.debug("Unknown status %d, using NOSTATE", status)
status = libvirt.VIR_DOMAIN_NOSTATE
return vm_status_icons[status]
def is_current(self):
return self._backend.isCurrent()
def is_external(self):
if self.get_xmlobj().memory_type == "external":
return True
for disk in self.get_xmlobj().disks:
if disk.snapshot == "external":
return True
return False
class vmmDomain(vmmLibvirtObject):
"""
Class wrapping virDomain libvirt objects. Is also extended to be
backed by a virtinst.Guest object for new VM 'customize before install'
"""
__gsignals__ = {
"status-changed": (GObject.SignalFlags.RUN_FIRST, None, [int, int]),
"resources-sampled": (GObject.SignalFlags.RUN_FIRST, None, []),
"inspection-changed": (GObject.SignalFlags.RUN_FIRST, None, []),
"pre-startup": (GObject.SignalFlags.RUN_FIRST, None, [object]),
}
@staticmethod
def pretty_run_status(status, has_saved=False):
if status == libvirt.VIR_DOMAIN_RUNNING:
return _("Running")
elif status == libvirt.VIR_DOMAIN_PAUSED:
return _("Paused")
elif status == libvirt.VIR_DOMAIN_SHUTDOWN:
return _("Shutting Down")
elif status == libvirt.VIR_DOMAIN_SHUTOFF:
if has_saved:
return _("Saved")
else:
return _("Shutoff")
elif status == libvirt.VIR_DOMAIN_CRASHED:
return _("Crashed")
elif (hasattr(libvirt, "VIR_DOMAIN_PMSUSPENDED") and
status == libvirt.VIR_DOMAIN_PMSUSPENDED):
return _("Suspended")
logging.debug("Unknown status %d, returning 'Unknown'", status)
return _("Unknown")
def __init__(self, conn, backend, key):
vmmLibvirtObject.__init__(self, conn, backend, key, Guest)
self.uuid = key
self.cloning = False
self.record = []
self.maxRecord = {
"diskRdRate" : 10.0,
"diskWrRate" : 10.0,
"netTxRate" : 10.0,
"netRxRate" : 10.0,
}
self._install_abort = False
self.reboot_listener = None
self._startup_vcpus = None
self._is_management_domain = None
self._id = None
self._name = None
self._snapshot_list = None
self.lastStatus = libvirt.VIR_DOMAIN_SHUTOFF
self.managedsave_supported = False
self.remote_console_supported = False
self.title_supported = False
self.mem_stats_supported = False
self._enable_mem_stats = False
self._enable_net_poll = False
self._stats_net_supported = True
self._stats_net_skip = []
self._enable_disk_poll = False
self._stats_disk_supported = True
self._stats_disk_skip = []
self.inspection = vmmInspectionData()
if isinstance(self._backend, Guest):
return
self._libvirt_init()
def _cleanup(self):
for snap in self._snapshot_list or []:
snap.cleanup()
self._snapshot_list = None
def _libvirt_init(self):
"""
Initialization to do if backed by a libvirt virDomain
"""
self.managedsave_supported = self.conn.check_support(
self.conn.SUPPORT_DOMAIN_MANAGED_SAVE, self._backend)
self.remote_console_supported = self.conn.check_support(
self.conn.SUPPORT_DOMAIN_CONSOLE_STREAM, self._backend)
self.title_supported = self.conn.check_support(
self.conn.SUPPORT_DOMAIN_GET_METADATA, self._backend)
self.mem_stats_supported = self.conn.check_support(
self.conn.SUPPORT_DOMAIN_MEMORY_STATS, self._backend)
# Determine available XML flags (older libvirt versions will error
# out if passed SECURE_XML, INACTIVE_XML, etc)
(self._inactive_xml_flags,
self._active_xml_flags) = self.conn.get_dom_flags(self._backend)
self.toggle_sample_network_traffic()
self.toggle_sample_disk_io()
self.toggle_sample_mem_stats()
self.force_update_status()
# Hook up listeners that need to be cleaned up
self.add_gconf_handle(
self.config.on_stats_enable_net_poll_changed(
self.toggle_sample_network_traffic))
self.add_gconf_handle(
self.config.on_stats_enable_disk_poll_changed(
self.toggle_sample_disk_io))
self.add_gconf_handle(
self.config.on_stats_enable_memory_poll_changed(
self.toggle_sample_mem_stats))
self.connect("status-changed", self._update_start_vcpus)
self.connect("pre-startup", self._prestartup_nodedev_check)
def _prestartup_nodedev_check(self, src, ret):
ignore = src
error = _("There is more than one '%s' device attached to "
"your host, and we can't determine which one to "
"use for your guest.\n"
"To fix this, remove and reattach the USB device "
"to your guest using the 'Add Hardware' wizard.")
for hostdev in self.get_hostdev_devices():
devtype = hostdev.type
if devtype != "usb":
continue
vendor = hostdev.vendor
product = hostdev.product
bus = hostdev.bus
device = hostdev.device
if vendor and product:
count = self.conn.get_nodedevs_number("usb_device",
vendor,
product)
if count > 1 and not (bus and device):
prettyname = "%s %s" % (vendor, product)
ret.append(error % prettyname)
###########################
# Misc API getter methods #
###########################
def get_name(self):
if self._name is None:
self._name = self._backend.name()
return self._name
def get_name_or_title(self):
title = self.get_title()
if title:
return title
return self.get_name()
def get_title(self):
return self.get_xmlobj(inactive=True).title
def get_id(self):
if self._id is None:
self._id = self._backend.ID()
return self._id
def status(self):
return self.lastStatus
def get_cloning(self):
return self.cloning
def set_cloning(self, val):
self.cloning = bool(val)
# If manual shutdown or destroy specified, make sure we don't continue
# install process
def get_install_abort(self):
return bool(self._install_abort)
def stable_defaults(self):
return self.conn.stable_defaults(self.get_emulator())
def is_read_only(self):
if self.is_management_domain():
return True
return False
def is_management_domain(self):
if self._is_management_domain is None:
self._is_management_domain = (self.get_id() == 0)
return self._is_management_domain
def has_spicevmc_type_redirdev(self):
devs = self.get_redirdev_devices()
for dev in devs:
if dev.type == "spicevmc":
return True
return False
def get_id_pretty(self):
i = self.get_id()
if i < 0:
return "-"
return str(i)
##################
# Support checks #
##################
def _get_getvcpus_supported(self):
return self.conn.check_support(
self.conn.SUPPORT_DOMAIN_GETVCPUS, self._backend)
getvcpus_supported = property(_get_getvcpus_supported)
def _get_getjobinfo_supported(self):
return self.conn.check_support(
self.conn.SUPPORT_DOMAIN_JOB_INFO, self._backend)
getjobinfo_supported = property(_get_getjobinfo_supported)
def snapshots_supported(self):
if not self.conn.check_support(
self.conn.SUPPORT_DOMAIN_LIST_SNAPSHOTS, self._backend):
return _("Libvirt connection does not support snapshots.")
if self.list_snapshots():
return
# Check if our disks are all qcow2
seen_qcow2 = False
for disk in self.get_disk_devices(refresh_if_nec=False):
if disk.read_only:
continue
if not disk.path:
continue
if disk.driver_type == "qcow2":
seen_qcow2 = True
continue
return _("Snapshots are only supported if all writeable disks "
"images allocated to the guest are qcow2 format.")
if not seen_qcow2:
return _("Snapshots require at least one writeable qcow2 disk "
"image allocated to the guest.")
#############################
# Internal XML handling API #
#############################
def _invalidate_xml(self):
vmmLibvirtObject._invalidate_xml(self)
self._name = None
self._id = None
def _lookup_device_to_define(self, origdev, guest=None):
if guest is None:
guest = self._get_xmlobj_to_define()
dev = _find_device(guest, origdev)
if dev:
return dev
# If we are removing multiple dev from an active VM, a double
# attempt may result in a lookup failure. If device is present
# in the active XML, assume all is good.
if _find_device(self.get_xmlobj(), origdev):
logging.debug("Device in active config but not inactive config.")
return
raise RuntimeError(_("Could not find specified device in the "
"inactive VM configuration: %s") % repr(origdev))
def _redefine_device(self, cb, origdev):
dev = self._lookup_device_to_define(origdev)
if dev:
return cb(dev)
##############################
# Persistent XML change APIs #
##############################
def define_name(self, newname):
return self._define_name_helper("domain",
self.conn.rename_vm,
newname)
# Device Add/Remove
def add_device(self, devobj):
"""
Redefine guest with appended device XML 'devxml'
"""
def change(guest):
guest.add_device(devobj)
ret = self._redefine(change)
self.redefine_cached()
return ret
def remove_device(self, devobj):
"""
Remove passed device from the inactive guest XML
"""
# HACK: If serial and console are both present, they both need
# to be removed at the same time
con = None
if hasattr(devobj, "virtmanager_console_dup"):
con = getattr(devobj, "virtmanager_console_dup")
def change(guest):
def rmdev(editdev):
if con:
rmcon = _find_device(guest, con)
if rmcon:
guest.remove_device(rmcon)
guest.remove_device(editdev)
return self._redefine_device(rmdev, devobj)
ret = self._redefine(change)
self.redefine_cached()
return ret
# CPU define methods
def define_vcpus(self, vcpus, maxvcpus):
def change(guest):
guest.curvcpus = int(vcpus)
guest.vcpus = int(maxvcpus)
return self._redefine(change)
def define_cpuset(self, cpuset):
def change(guest):
guest.cpuset = cpuset
return self._redefine(change)
def define_cpu_topology(self, sockets, cores, threads):
def change(guest):
cpu = guest.cpu
cpu.sockets = sockets
cpu.cores = cores
cpu.threads = threads
return self._redefine(change)
def define_cpu(self, val):
def change(guest):
if val in guest.cpu.SPECIAL_MODES:
guest.cpu.set_special_mode(val)
else:
guest.cpu.model = val
return self._redefine(change)
# Mem define methods
def define_both_mem(self, memory, maxmem):
def change(guest):
guest.memory = int(memory)
guest.maxmemory = int(maxmem)
return self._redefine(change)
# Machine config define methods
def define_machtype(self, newvalue):
def change(guest):
guest.os.machine = newvalue
return self._redefine(change)
def define_description(self, newvalue):
def change(guest):
guest.description = newvalue or None
return self._redefine(change)
def define_title(self, newvalue):
def change(guest):
guest.title = newvalue or None
return self._redefine(change)
# Boot define methods
def can_use_device_boot_order(self):
# Return 'True' if guest can use new style boot device ordering
return self.conn.check_support(
self.conn.SUPPORT_CONN_DEVICE_BOOTORDER)
def get_bootable_devices(self):
devs = self.get_disk_devices()
devs += self.get_network_devices()
devs += self.get_hostdev_devices()
# redirdev can also be marked bootable, but it should be rarely
# used and clutters the UI
return devs
def _set_device_boot_order(self, boot_list):
boot_dev_order = []
devmap = dict((dev.vmmidstr, dev) for dev in
self.get_bootable_devices())
for b in boot_list:
if b in devmap:
boot_dev_order.append(devmap[b])
def change(guest):
# Unset the traditional boot order
guest.os.bootorder = []
# Unset standard boot order
for dev in guest.get_all_devices():
dev.boot.order = None
count = 1
for origdev in boot_dev_order:
dev = self._lookup_device_to_define(origdev, guest=guest)
if not dev:
continue
dev.boot.order = count
count += 1
return self._redefine(change)
def set_boot_order(self, boot_list):
if self.can_use_device_boot_order():
return self._set_device_boot_order(boot_list)
def change(guest):
guest.os.bootorder = boot_list
return self._redefine(change)
def set_boot_menu(self, newval):
def change(guest):
guest.os.enable_bootmenu = bool(newval)
return self._redefine(change)
def set_boot_kernel(self, kernel, initrd, dtb, args):
def change(guest):
guest.os.kernel = kernel or None
guest.os.initrd = initrd or None
guest.os.dtb = dtb or None
guest.os.kernel_args = args or None
return self._redefine(change)
def set_boot_init(self, init):
def change(guest):
guest.os.init = init
return self._redefine(change)
# Disk define methods
def define_storage_media(self, devobj, newpath):
def change(editdev):
editdev.path = newpath
editdev.sync_path_props()
return self._redefine_device(change, devobj)
def define_disk_readonly(self, devobj, do_readonly):
def change(editdev):
editdev.read_only = do_readonly
return self._redefine_device(change, devobj)
def define_disk_shareable(self, devobj, do_shareable):
def change(editdev):
editdev.shareable = do_shareable
return self._redefine_device(change, devobj)
def define_disk_removable(self, devobj, do_removable):
def change(editdev):
editdev.removable = do_removable
return self._redefine_device(change, devobj)
def define_disk_cache(self, devobj, new_cache):
def change(editdev):
editdev.driver_cache = new_cache or None
return self._redefine_device(change, devobj)
def define_disk_io(self, devobj, val):
def change(editdev):
editdev.driver_io = val or None
return self._redefine_device(change, devobj)
def define_disk_driver_type(self, devobj, new_driver_type):
def change(editdev):
editdev.driver_type = new_driver_type or None
return self._redefine_device(change, devobj)
def define_disk_bus(self, devobj, newval, addr):
def change(editdev):
oldprefix = editdev.get_target_prefix()[0]
oldbus = editdev.bus
editdev.bus = newval
if oldbus == newval:
return
editdev.address.clear()
editdev.address.set_addrstr(addr)
if oldprefix == editdev.get_target_prefix()[0]:
return
used = []
disks = (self.get_disk_devices() +
self.get_disk_devices(inactive=True))
for d in disks:
used.append(d.target)
if editdev.target:
used.remove(editdev.target)
editdev.target = None
editdev.generate_target(used)
return self._redefine_device(change, devobj)
def define_disk_serial(self, devobj, val):
def change(editdev):
if val != editdev.serial:
editdev.serial = val or None
return self._redefine_device(change, devobj)
def define_disk_iotune_rbs(self, devobj, val):
def change(editdev):
editdev.iotune_rbs = val
return self._redefine_device(change, devobj)
def define_disk_iotune_ris(self, devobj, val):
def change(editdev):
editdev.iotune_ris = val
return self._redefine_device(change, devobj)
def define_disk_iotune_tbs(self, devobj, val):
def change(editdev):
editdev.iotune_tbs = val
return self._redefine_device(change, devobj)
def define_disk_iotune_tis(self, devobj, val):
def change(editdev):
editdev.iotune_tis = val
return self._redefine_device(change, devobj)
def define_disk_iotune_wbs(self, devobj, val):
def change(editdev):
editdev.iotune_wbs = val
return self._redefine_device(change, devobj)
def define_disk_iotune_wis(self, devobj, val):
def change(editdev):
editdev.iotune_wis = val
return self._redefine_device(change, devobj)
# Network define methods
def define_network_source(self, devobj, newtype, newsource, newmode):
def change(editdev):
if not newtype:
return
editdev.source = None
editdev.type = newtype
editdev.source = newsource
editdev.source_mode = newmode or None
return self._redefine_device(change, devobj)
def define_network_model(self, devobj, newmodel, addr):
def change(editdev):
if editdev.model != newmodel:
editdev.address.clear()
editdev.address.set_addrstr(addr)
editdev.model = newmodel
return self._redefine_device(change, devobj)
def define_virtualport(self, devobj, newtype, newmanagerid,
newtypeid, newtypeidversion, newinstanceid):
def change(editdev):
editdev.virtualport.type = newtype or None
editdev.virtualport.managerid = newmanagerid or None
editdev.virtualport.typeid = newtypeid or None
editdev.virtualport.typeidversion = newtypeidversion or None
editdev.virtualport.instanceid = newinstanceid or None
return self._redefine_device(change, devobj)
# Graphics define methods
def define_graphics_address(self, devobj, newval):
def change(editdev):
editdev.listen = newval
return self._redefine_device(change, devobj)
def define_graphics_port(self, devobj, newval):
def change(editdev):
editdev.port = newval
return self._redefine_device(change, devobj)
def define_graphics_tlsport(self, devobj, newval):
def change(editdev):
editdev.tlsPort = newval
return self._redefine_device(change, devobj)
def define_graphics_password(self, devobj, newval):
def change(editdev):
editdev.passwd = newval
return self._redefine_device(change, devobj)
def define_graphics_keymap(self, devobj, newval):
def change(editdev):
editdev.keymap = newval
return self._redefine_device(change, devobj)
def define_graphics_type(self, devobj, newval):
def change(editdev):
editdev.type = newval
return self._redefine_device(change, devobj)
# Sound define methods
def define_sound_model(self, devobj, newmodel):
def change(editdev):
if editdev.model != newmodel:
editdev.address.clear()
editdev.model = newmodel
return self._redefine_device(change, devobj)
# Video define methods
def define_video_model(self, devobj, newmodel):
def change(editdev):
if newmodel == editdev.model:
return
editdev.model = newmodel
editdev.address.clear()
# Clear out heads/ram values so they reset to default. If
# we ever allow editing these values in the UI we should
# drop this
editdev.vram = None
editdev.heads = None
editdev.ram = None
return self._redefine_device(change, devobj)
# Watchdog define methods
def define_watchdog_model(self, devobj, newval):
def change(editdev):
if editdev.model != newval:
editdev.address.clear()
editdev.model = newval
return self._redefine_device(change, devobj)
def define_watchdog_action(self, devobj, newval):
def change(editdev):
editdev.action = newval
return self._redefine_device(change, devobj)
# Smartcard define methods
def define_smartcard_mode(self, devobj, newmodel):
def change(editdev):
editdev.mode = newmodel
editdev.type = editdev.TYPE_DEFAULT
return self._redefine_device(change, devobj)
# Controller define methods
def define_controller_model(self, devobj, newmodel):
def change(editdev):
ignore = editdev
if editdev.type == "usb":
guest = self._get_xmlobj_to_define()
ctrls = guest.get_devices("controller")
ctrls = [x for x in ctrls if (x.type ==
VirtualController.TYPE_USB)]
for dev in ctrls:
guest.remove_device(dev)
if newmodel == "ich9-ehci1":
for dev in VirtualController.get_usb2_controllers(
guest.conn):
guest.add_device(dev)
else:
dev = VirtualController(guest.conn)
dev.type = "usb"
if newmodel != "default":
dev.model = newmodel
guest.add_device(dev)
elif editdev.type == "scsi":
if newmodel == "default":
editdev.model = None
else:
editdev.model = newmodel
self.update_device(editdev)
return self._redefine_device(change, devobj)
def define_filesystem(self, devobj, newdev):
def change(editdev):
editdev.type = newdev.type
editdev.mode = newdev.mode
editdev.wrpolicy = newdev.wrpolicy
editdev.driver = newdev.driver
editdev.format = newdev.format
editdev.readonly = newdev.readonly
editdev.units = newdev.units
editdev.source = newdev.source
editdev.target = newdev.target
return self._redefine_device(change, devobj)
def define_hostdev_rombar(self, devobj, val):
def change(editdev):
editdev.rom_bar = val
return self._redefine_device(change, devobj)
####################
# Hotplug routines #
####################
def attach_device(self, devobj):
"""
Hotplug device to running guest
"""
if not self.is_active():
return
devxml = devobj.get_xml_config()
self._backend.attachDevice(devxml)
def detach_device(self, devobj):
"""
Hotunplug device from running guest
"""
if not self.is_active():
return
xml = devobj.get_xml_config()
self._backend.detachDevice(xml)
def update_device(self, devobj, flags=1):
if not self.is_active():
return
# Default flag is VIR_DOMAIN_DEVICE_MODIFY_LIVE
xml = devobj.get_xml_config()
self._backend.updateDeviceFlags(xml, flags)
def hotplug_vcpus(self, vcpus):
vcpus = int(vcpus)
if vcpus != self.vcpu_count():
self._backend.setVcpus(vcpus)
def hotplug_memory(self, memory):
if memory != self.get_memory():
self._backend.setMemory(memory)
def hotplug_maxmem(self, maxmem):
if maxmem != self.maximum_memory():
self._backend.setMaxMemory(maxmem)
def hotplug_both_mem(self, memory, maxmem):
logging.info("Hotplugging curmem=%s maxmem=%s for VM '%s'",
memory, maxmem, self.get_name())
if self.is_active():
actual_cur = self.get_memory()
if memory:
if maxmem < actual_cur:
# Set current first to avoid error
self.hotplug_memory(memory)
self.hotplug_maxmem(maxmem)
else:
self.hotplug_maxmem(maxmem)
self.hotplug_memory(memory)
else:
self.hotplug_maxmem(maxmem)
def hotplug_storage_media(self, devobj, newpath):
devobj.path = newpath
self.attach_device(devobj)
def hotplug_graphics_password(self, devobj, newval):
devobj.passwd = newval
self.update_device(devobj)
def hotplug_description(self, desc):
# We already fake hotplug like behavior, by reading the
# description from the inactive XML from a running VM
#
# libvirt since 0.9.10 provides a SetMetadata API that provides
# actual <description> 'hotplug', and using that means checkig
# for support, version, etc.
if not self.conn.check_support(
self.conn.SUPPORT_DOMAIN_SET_METADATA, self._backend):
return
flags = (libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_AFFECT_CONFIG)
self._backend.setMetadata(
libvirt.VIR_DOMAIN_METADATA_DESCRIPTION,
desc, None, None, flags)
def hotplug_title(self, title):
if not self.conn.check_support(
self.conn.SUPPORT_DOMAIN_SET_METADATA, self._backend):
return
flags = (libvirt.VIR_DOMAIN_AFFECT_LIVE |
libvirt.VIR_DOMAIN_AFFECT_CONFIG)
self._backend.setMetadata(
libvirt.VIR_DOMAIN_METADATA_TITLE,
title, None, None, flags)
########################
# Libvirt API wrappers #
########################
def _define(self, newxml):
self.conn.define_domain(newxml)
def _XMLDesc(self, flags):
return self._backend.XMLDesc(flags)
def get_autostart(self):
return self._backend.autostart()
def set_autostart(self, val):
if self.get_autostart() == val:
return
self._backend.setAutostart(val)
def job_info(self):
return self._backend.jobInfo()
def abort_job(self):
self._backend.abortJob()
def open_console(self, devname, stream, flags=0):
return self._backend.openConsole(devname, stream, flags)
def refresh_snapshots(self):
self._snapshot_list = None
def list_snapshots(self):
if self._snapshot_list is None:
newlist = []
for rawsnap in self._backend.listAllSnapshots():
newlist.append(vmmDomainSnapshot(self.conn, rawsnap))
self._snapshot_list = newlist
return self._snapshot_list[:]
def revert_to_snapshot(self, snap):
self._backend.revertToSnapshot(snap.get_backend())
self.idle_add(self.force_update_status)
def create_snapshot(self, xml, redefine=False):
flags = 0
if redefine:
flags = (flags | libvirt.VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)
if not redefine:
logging.debug("Creating snapshot flags=%s xml=\n%s", flags, xml)
self._backend.snapshotCreateXML(xml, flags)
########################
# XML Parsing routines #
########################
def is_container(self):
return self.get_xmlobj().os.is_container()
def is_xenpv(self):
return self.get_xmlobj().os.is_xenpv()
def is_hvm(self):
return self.get_xmlobj().os.is_hvm()
def get_uuid(self):
return self.uuid
def get_abi_type(self):
return self.get_xmlobj().os.os_type
def get_hv_type(self):
return self.get_xmlobj().type
def get_pretty_hv_type(self):
return self.conn.pretty_hv(self.get_abi_type(), self.get_hv_type())
def get_arch(self):
return self.get_xmlobj().os.arch
def get_init(self):
return self.get_xmlobj().os.init
def get_emulator(self):
return self.get_xmlobj().emulator
def get_machtype(self):
return self.get_xmlobj().os.machine
def get_description(self):
# Always show the inactive <description>, let's us fake hotplug
# for a field that's strictly metadata
return self.get_xmlobj(inactive=True).description
def get_memory(self):
return int(self.get_xmlobj().memory)
def maximum_memory(self):
return int(self.get_xmlobj().maxmemory)
def vcpu_count(self):
guest = self.get_xmlobj()
return int(guest.curvcpus or
self._startup_vcpus or
guest.vcpus)
def vcpu_max_count(self):
guest = self.get_xmlobj()
has_xml_max = (guest.curvcpus != guest.vcpus)
if has_xml_max or not self.is_active():
return guest.vcpus
if self._startup_vcpus is None:
self._startup_vcpus = int(self.vcpu_count())
return int(self._startup_vcpus)
def vcpu_pinning(self):
return self.get_xmlobj().cpuset or ""
def get_cpu_config(self):
return self.get_xmlobj().cpu
def _convert_old_boot_order(self):
boot_order = self._get_old_boot_order()
ret = []
disks = self.get_disk_devices()
nets = self.get_network_devices()
for b in boot_order:
if b == "network":
ret += [n.vmmidstr for n in nets]
if b == "hd":
ret += [d.vmmidstr for d in disks if
d.device not in ["cdrom", "floppy"]]
if b == "cdrom":
ret += [d.vmmidstr for d in disks if d.device == "cdrom"]
if b == "floppy":
ret += [d.vmmidstr for d in disks if d.device == "floppy"]
return ret
def _get_device_boot_order(self):
devs = self.get_bootable_devices()
order = []
for dev in devs:
if not dev.boot.order:
continue
order.append((dev.vmmidstr, dev.boot.order))
if not order:
# No devices individually marked bootable, convert traditional
# boot XML to fine grained, for the UI.
return self._convert_old_boot_order()
order.sort(key=lambda p: p[1])
return [p[0] for p in order]
def _get_old_boot_order(self):
return self.get_xmlobj().os.bootorder
def get_boot_order(self):
if self.can_use_device_boot_order():
return self._get_device_boot_order()
return self._get_old_boot_order()
def get_boot_menu(self):
guest = self.get_xmlobj()
return bool(guest.os.enable_bootmenu)
def get_boot_kernel_info(self):
guest = self.get_xmlobj()
return (guest.os.kernel, guest.os.initrd,
guest.os.dtb, guest.os.kernel_args)
# XML Device listing
def get_serial_devs(self):
devs = self.get_char_devices()
devlist = []
devlist += [x for x in devs if x.virtual_device_type == "serial"]
devlist += [x for x in devs if x.virtual_device_type == "console"]
return devlist
def _build_device_list(self, device_type,
refresh_if_nec=True, inactive=False):
guest = self.get_xmlobj(refresh_if_nec=refresh_if_nec,
inactive=inactive)
devs = guest.get_devices(device_type)
for idx in range(len(devs)):
devs[idx].vmmindex = idx
devs[idx].vmmidstr = devs[idx].virtual_device_type + ("%.3d" % idx)
return devs
def get_network_devices(self, refresh_if_nec=True):
return self._build_device_list("interface", refresh_if_nec)
def get_video_devices(self):
return self._build_device_list("video")
def get_hostdev_devices(self):
return self._build_device_list("hostdev")
def get_watchdog_devices(self):
return self._build_device_list("watchdog")
def get_input_devices(self):
return self._build_device_list("input")
def get_graphics_devices(self):
return self._build_device_list("graphics")
def get_sound_devices(self):
return self._build_device_list("sound")
def get_controller_devices(self):
return self._build_device_list("controller")
def get_filesystem_devices(self):
return self._build_device_list("filesystem")
def get_smartcard_devices(self):
return self._build_device_list("smartcard")
def get_redirdev_devices(self):
return self._build_device_list("redirdev")
def get_tpm_devices(self):
return self._build_device_list("tpm")
def get_rng_devices(self):
return self._build_device_list("rng")
def get_panic_devices(self):
return self._build_device_list("panic")
def get_disk_devices(self, refresh_if_nec=True, inactive=False):
devs = self._build_device_list("disk", refresh_if_nec, inactive)
# Iterate through all disks and calculate what number they are
# HACK: We are making a variable in VirtualDisk to store the index
idx_mapping = {}
for dev in devs:
devtype = dev.device
bus = dev.bus
key = devtype + (bus or "")
if key not in idx_mapping:
idx_mapping[key] = 1
dev.disk_bus_index = idx_mapping[key]
idx_mapping[key] += 1
return devs
def get_char_devices(self):
devs = []
serials = self._build_device_list("serial")
parallels = self._build_device_list("parallel")
consoles = self._build_device_list("console")
channels = self._build_device_list("channel")
for devicelist in [serials, parallels, consoles, channels]:
devs.extend(devicelist)
# Don't display <console> if it's just a duplicate of <serial>
if (len(consoles) > 0 and len(serials) > 0):
con = consoles[0]
ser = serials[0]
if (con.type == ser.type and
con.target_type is None or con.target_type == "serial"):
ser.virtmanager_console_dup = con
devs.remove(con)
return devs
############################
# Domain lifecycle methods #
############################
# All these methods are usually run asynchronously from threads, so
# let's be extra careful and have anything which might touch UI
# or GObject.props invoked in an idle callback
def _unregister_reboot_listener(self):
if self.reboot_listener is None:
return
try:
self.idle_add(self.disconnect, self.reboot_listener)
self.reboot_listener = None
except:
pass
def manual_reboot(self):
"""
Attempt a manual reboot by invoking 'shutdown', then listen
for a state change and restart the VM
"""
def reboot_listener(vm, ignore1, ignore2, self):
if vm.is_crashed():
# Abandon reboot plans
self.reboot_listener = None
return True
if not vm.is_shutoff():
# Not shutoff, continue waiting
return
try:
logging.debug("Fake reboot detected shutdown. Restarting VM")
vm.startup()
except:
logging.exception("Fake reboot startup failed")
self.reboot_listener = None
return True
self._unregister_reboot_listener()
# Request a shutdown
self.shutdown()
def add_reboot():
self.reboot_listener = self.connect_opt_out("status-changed",
reboot_listener, self)
self.idle_add(add_reboot)
def shutdown(self):
self._install_abort = True
self._unregister_reboot_listener()
self._backend.shutdown()
self.idle_add(self.force_update_status)
def reboot(self):
self._install_abort = True
self._backend.reboot(0)
self.idle_add(self.force_update_status)
def destroy(self):
self._install_abort = True
self._unregister_reboot_listener()
self._backend.destroy()
self.idle_add(self.force_update_status)
def reset(self):
self._install_abort = True
self._backend.reset(0)
self.idle_add(self.force_update_status)
def startup(self):
if self.get_cloning():
raise RuntimeError(_("Cannot start guest while cloning "
"operation in progress"))
pre_startup_ret = []
self.emit("pre-startup", pre_startup_ret)
for error in pre_startup_ret:
raise RuntimeError(error)
self._backend.create()
self.idle_add(self.force_update_status)
def suspend(self):
self._backend.suspend()
self.idle_add(self.force_update_status)
def delete(self, force=True):
flags = 0
if force:
flags |= getattr(libvirt,
"VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA", 0)
flags |= getattr(libvirt, "VIR_DOMAIN_UNDEFINE_MANAGED_SAVE", 0)
try:
self._backend.undefineFlags(flags)
except libvirt.libvirtError:
logging.exception("libvirt undefineFlags failed, "
"falling back to old style")
self._backend.undefine()
def resume(self):
if self.get_cloning():
raise RuntimeError(_("Cannot resume guest while cloning "
"operation in progress"))
self._backend.resume()
self.idle_add(self.force_update_status)
def hasSavedImage(self):
if not self.managedsave_supported:
return False
try:
return self._backend.hasManagedSaveImage(0)
except libvirt.libvirtError, e:
if not util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"):
raise
return False
def removeSavedImage(self):
if not self.hasSavedImage():
return
self._backend.managedSaveRemove(0)
def save(self, filename=None, meter=None):
self._install_abort = True
if meter:
start_job_progress_thread(self, meter, _("Saving domain to disk"))
if not self.managedsave_supported:
self._backend.save(filename)
else:
self._backend.managedSave(0)
self.idle_add(self.force_update_status)
def support_downtime(self):
return self.conn.check_support(
self.conn.SUPPORT_DOMAIN_MIGRATE_DOWNTIME, self._backend)
def migrate_set_max_downtime(self, max_downtime, flag=0):
self._backend.migrateSetMaxDowntime(max_downtime, flag)
def migrate(self, destconn, interface=None, rate=0,
live=False, secure=False, unsafe=False, meter=None):
self._install_abort = True
newname = None
flags = 0
if live:
flags |= libvirt.VIR_MIGRATE_LIVE
if secure:
flags |= libvirt.VIR_MIGRATE_PEER2PEER
flags |= libvirt.VIR_MIGRATE_TUNNELLED
if unsafe:
flags |= libvirt.VIR_MIGRATE_UNSAFE
destconn = destconn.get_backend().libvirtconn
logging.debug("Migrating: conn=%s flags=%s dname=%s uri=%s rate=%s",
destconn, flags, newname, interface, rate)
if meter:
start_job_progress_thread(self, meter, _("Migrating domain"))
self._backend.migrate(destconn, flags, newname, interface, rate)
def define_cb():
newxml = self.get_xml(inactive=True)
destconn.define_domain(newxml)
self.idle_add(define_cb)
# Don't schedule any conn update, migrate dialog handles it for us
#################
# Stats helpers #
#################
def _sample_cpu_stats(self, info, now):
prevCpuTime = 0
prevTimestamp = 0
cpuTime = 0
cpuTimeAbs = 0
pcentHostCpu = 0
pcentGuestCpu = 0
if len(self.record) > 0:
prevTimestamp = self.record[0]["timestamp"]
prevCpuTime = self.record[0]["cpuTimeAbs"]
if not (info[0] in [libvirt.VIR_DOMAIN_SHUTOFF,
libvirt.VIR_DOMAIN_CRASHED]):
guestcpus = info[3]
cpuTime = info[4] - prevCpuTime
cpuTimeAbs = info[4]
hostcpus = self.conn.host_active_processor_count()
pcentbase = (((cpuTime) * 100.0) /
((now - prevTimestamp) * 1000.0 * 1000.0 * 1000.0))
pcentHostCpu = pcentbase / hostcpus
pcentGuestCpu = pcentbase / guestcpus
pcentHostCpu = max(0.0, min(100.0, pcentHostCpu))
pcentGuestCpu = max(0.0, min(100.0, pcentGuestCpu))
return cpuTime, cpuTimeAbs, pcentHostCpu, pcentGuestCpu
def _get_cur_rate(self, what):
if len(self.record) > 1:
ret = (float(self.record[0][what] -
self.record[1][what]) /
float(self.record[0]["timestamp"] -
self.record[1]["timestamp"]))
else:
ret = 0.0
return max(ret, 0, 0) # avoid negative values at poweroff
def _set_max_rate(self, record, what):
if record[what] > self.maxRecord[what]:
self.maxRecord[what] = record[what]
def _get_max_rate(self, name1, name2):
return float(max(self.maxRecord[name1], self.maxRecord[name2]))
def _get_record_helper(self, record_name):
if len(self.record) == 0:
return 0
return self.record[0][record_name]
def _vector_helper(self, record_name):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length() + 1):
if i < len(stats):
vector.append(stats[i][record_name] / 100.0)
else:
vector.append(0)
return vector
def _in_out_vector_helper(self, name1, name2, ceil):
vector = []
stats = self.record
if ceil is None:
ceil = self._get_max_rate(name1, name2)
maxlen = self.config.get_stats_history_length()
for n in [name1, name2]:
for i in range(maxlen + 1):
if i < len(stats):
vector.append(float(stats[i][n]) / ceil)
else:
vector.append(0.0)
return vector
def in_out_vector_limit(self, data, limit):
l = len(data) / 2
end = min(l, limit)
if l > limit:
data = data[0:end] + data[l:l + end]
return [(x + y) / 2 for x, y in zip(data[0:end], data[end:end * 2])]
def toggle_sample_network_traffic(self, ignore=None):
self._enable_net_poll = self.config.get_stats_enable_net_poll()
if self._enable_net_poll and len(self.record) > 1:
rxBytes, txBytes = self._sample_network_traffic()
self.record[0]["netRxKB"] = rxBytes / 1024
self.record[0]["netTxKB"] = txBytes / 1024
def toggle_sample_disk_io(self, ignore=None):
self._enable_disk_poll = self.config.get_stats_enable_disk_poll()
if self._enable_disk_poll and len(self.record) > 1:
rdBytes, wrBytes = self._sample_disk_io()
self.record[0]["diskRdKB"] = rdBytes / 1024
self.record[0]["diskWrKB"] = wrBytes / 1024
def toggle_sample_mem_stats(self, ignore=None):
self._enable_mem_stats = self.config.get_stats_enable_memory_poll()
###################
# Stats accessors #
###################
def stats_memory(self):
return self._get_record_helper("curmem")
def cpu_time(self):
return self._get_record_helper("cpuTime")
def host_cpu_time_percentage(self):
return self._get_record_helper("cpuHostPercent")
def guest_cpu_time_percentage(self):
return self._get_record_helper("cpuGuestPercent")
def network_rx_rate(self):
return self._get_record_helper("netRxRate")
def network_tx_rate(self):
return self._get_record_helper("netTxRate")
def disk_read_rate(self):
return self._get_record_helper("diskRdRate")
def disk_write_rate(self):
return self._get_record_helper("diskWrRate")
def get_memory_pretty(self):
return util.pretty_mem(self.get_memory())
def maximum_memory_pretty(self):
return util.pretty_mem(self.maximum_memory())
def network_traffic_rate(self):
return self.network_tx_rate() + self.network_rx_rate()
def network_traffic_max_rate(self):
return self._get_max_rate("netRxRate", "netTxRate")
def disk_io_rate(self):
return self.disk_read_rate() + self.disk_write_rate()
def disk_io_max_rate(self):
return self._get_max_rate("diskRdRate", "diskWrRate")
def host_cpu_time_vector(self):
return self._vector_helper("cpuHostPercent")
def guest_cpu_time_vector(self):
return self._vector_helper("cpuGuestPercent")
def stats_memory_vector(self):
return self._vector_helper("currMemPercent")
def network_traffic_vector(self, ceil=None):
return self._in_out_vector_helper("netRxRate", "netTxRate", ceil)
def disk_io_vector(self, ceil=None):
return self._in_out_vector_helper("diskRdRate", "diskWrRate", ceil)
def host_cpu_time_vector_limit(self, limit):
cpudata = self.host_cpu_time_vector()
if len(cpudata) > limit:
cpudata = cpudata[0:limit]
return cpudata
def guest_cpu_time_vector_limit(self, limit):
cpudata = self.guest_cpu_time_vector()
if len(cpudata) > limit:
cpudata = cpudata[0:limit]
return cpudata
def memory_usage_vector_limit(self, limit):
return self.in_out_vector_limit(self.stats_memory_vector(),
limit)
def network_traffic_vector_limit(self, limit, ceil=None):
return self.in_out_vector_limit(self.network_traffic_vector(ceil),
limit)
def disk_io_vector_limit(self, limit, ceil=None):
return self.in_out_vector_limit(self.disk_io_vector(ceil), limit)
###################
# Status helpers ##
###################
def _update_start_vcpus(self, ignore, oldstatus, status):
ignore = status
if oldstatus not in [libvirt.VIR_DOMAIN_SHUTDOWN,
libvirt.VIR_DOMAIN_SHUTOFF,
libvirt.VIR_DOMAIN_CRASHED]:
return
# Want to track the startup vcpu amount, which is the
# cap of how many VCPUs can be added
self._startup_vcpus = None
self.vcpu_max_count()
def _normalize_status(self, status):
if status == libvirt.VIR_DOMAIN_NOSTATE:
return libvirt.VIR_DOMAIN_RUNNING
elif status == libvirt.VIR_DOMAIN_BLOCKED:
return libvirt.VIR_DOMAIN_RUNNING
return status
def is_active(self):
return not self.is_shutoff()
def is_shutoff(self):
return self.status() == libvirt.VIR_DOMAIN_SHUTOFF
def is_crashed(self):
return self.status() == libvirt.VIR_DOMAIN_CRASHED
def is_stoppable(self):
return self.status() in [libvirt.VIR_DOMAIN_RUNNING,
libvirt.VIR_DOMAIN_PAUSED]
def is_destroyable(self):
return (self.is_stoppable() or
self.status() in [libvirt.VIR_DOMAIN_CRASHED])
def is_runable(self):
return self.status() in [libvirt.VIR_DOMAIN_SHUTOFF,
libvirt.VIR_DOMAIN_CRASHED]
def is_pauseable(self):
return self.status() in [libvirt.VIR_DOMAIN_RUNNING]
def is_unpauseable(self):
return self.status() in [libvirt.VIR_DOMAIN_PAUSED]
def is_paused(self):
return self.status() in [libvirt.VIR_DOMAIN_PAUSED]
def run_status(self):
return self.pretty_run_status(self.status(), self.hasSavedImage())
def run_status_icon_name(self):
status = self.status()
if status not in vm_status_icons:
logging.debug("Unknown status %d, using NOSTATE", status)
status = libvirt.VIR_DOMAIN_NOSTATE
return vm_status_icons[status]
def force_update_status(self):
"""
Fetch current domain state and clear status cache
"""
try:
info = self._backend.info()
self._update_status(info[0])
except libvirt.libvirtError, e:
if util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"):
return
raise
def _update_status(self, status):
"""
Internal helper to change cached status to 'status' and signal
clients if we actually changed state
"""
status = self._normalize_status(status)
if status == self.lastStatus:
return
oldstatus = self.lastStatus
self.lastStatus = status
# Send 'config-changed' before a status-update, so users
# are operating with fresh XML
self.refresh_xml()
self.idle_emit("status-changed", oldstatus, status)
def inspection_data_updated(self):
self.idle_emit("inspection-changed")
##################
# config helpers #
##################
def on_console_scaling_changed(self, *args, **kwargs):
return self.config.listen_pervm(self.uuid, "/scaling",
*args, **kwargs)
def set_console_scaling(self, value):
self.config.set_pervm(self.uuid, "/scaling", value)
def get_console_scaling(self):
ret = self.config.get_pervm(self.uuid, "/scaling")
if ret == -1:
return self.config.get_console_scaling()
return ret
def on_console_resizeguest_changed(self, *args, **kwargs):
return self.config.listen_pervm(self.uuid, "/resize-guest",
*args, **kwargs)
def set_console_resizeguest(self, value):
self.config.set_pervm(self.uuid, "/resize-guest", value)
def get_console_resizeguest(self):
ret = self.config.get_pervm(self.uuid, "/resize-guest")
if ret == -1:
return self.config.get_console_resizeguest()
return ret
def set_details_window_size(self, w, h):
self.config.set_pervm(self.uuid, "/vm-window-size", (w, h))
def get_details_window_size(self):
ret = self.config.get_pervm(self.uuid, "/vm-window-size")
return ret
def get_console_password(self):
return self.config.get_pervm(self.uuid, "/console-password")
def set_console_password(self, username, keyid):
return self.config.set_pervm(self.uuid, "/console-password",
(username, keyid))
def get_cache_dir(self):
ret = os.path.join(self.conn.get_cache_dir(), self.get_uuid())
if not os.path.exists(ret):
os.makedirs(ret, 0755)
return ret
###################
# Polling helpers #
###################
def _sample_network_traffic(self):
rx = 0
tx = 0
if (not self._stats_net_supported or
not self._enable_net_poll or
not self.is_active()):
self._stats_net_skip = []
return rx, tx
for netdev in self.get_network_devices(refresh_if_nec=False):
dev = netdev.target_dev
if not dev:
continue
if dev in self._stats_net_skip:
continue
try:
io = self._backend.interfaceStats(dev)
if io:
rx += io[0]
tx += io[4]
except libvirt.libvirtError, err:
if util.is_error_nosupport(err):
logging.debug("Net stats not supported: %s", err)
self._stats_net_supported = False
else:
logging.error("Error reading net stats for "
"'%s' dev '%s': %s",
self.get_name(), dev, err)
if self.is_active():
logging.debug("Adding %s to skip list", dev)
self._stats_net_skip.append(dev)
else:
logging.debug("Aren't running, don't add to skiplist")
return rx, tx
def _sample_disk_io(self):
rd = 0
wr = 0
if (not self._stats_disk_supported or
not self._enable_disk_poll or
not self.is_active()):
self._stats_disk_skip = []
return rd, wr
for disk in self.get_disk_devices(refresh_if_nec=False):
dev = disk.target
if not dev:
continue
if dev in self._stats_disk_skip:
continue
try:
io = self._backend.blockStats(dev)
if io:
rd += io[1]
wr += io[3]
except libvirt.libvirtError, err:
if util.is_error_nosupport(err):
logging.debug("Disk stats not supported: %s", err)
self._stats_disk_supported = False
else:
logging.error("Error reading disk stats for "
"'%s' dev '%s': %s",
self.get_name(), dev, err)
if self.is_active():
logging.debug("Adding %s to skip list", dev)
self._stats_disk_skip.append(dev)
else:
logging.debug("Aren't running, don't add to skiplist")
return rd, wr
def _sample_mem_stats(self):
if (not self.mem_stats_supported or
not self._enable_mem_stats or
not self.is_active()):
return 0, 0
curmem = 0
totalmem = 1
try:
stats = self._backend.memoryStats()
# did we get both required stat items back?
if set(['actual', 'rss']).issubset(
set(stats.keys())):
curmem = stats['rss']
totalmem = stats['actual']
except libvirt.libvirtError, err:
logging.error("Error reading mem stats: %s", err)
pcentCurrMem = curmem * 100.0 / totalmem
pcentCurrMem = max(0.0, min(pcentCurrMem, 100.0))
return pcentCurrMem, curmem
def tick(self, stats_update=True):
self._invalidate_xml()
info = self._backend.info()
if stats_update:
self._tick_stats(info)
self._update_status(info[0])
if stats_update:
self.idle_emit("resources-sampled")
def _tick_stats(self, info):
expected = self.config.get_stats_history_length()
current = len(self.record)
if current > expected:
del self.record[expected:current]
# Xen reports complete crap for Dom0 max memory
# (ie MAX_LONG) so lets clamp it to the actual
# physical RAM in machine which is the effective
# real world limit
if (self.conn.is_xen() and
self.is_management_domain()):
info[1] = self.conn.host_memory_size()
now = time.time()
(cpuTime, cpuTimeAbs,
pcentHostCpu, pcentGuestCpu) = self._sample_cpu_stats(info, now)
pcentCurrMem, curmem = self._sample_mem_stats()
rdBytes, wrBytes = self._sample_disk_io()
rxBytes, txBytes = self._sample_network_traffic()
newStats = {
"timestamp": now,
"cpuTime": cpuTime,
"cpuTimeAbs": cpuTimeAbs,
"cpuHostPercent": pcentHostCpu,
"cpuGuestPercent": pcentGuestCpu,
"curmem": curmem,
"currMemPercent": pcentCurrMem,
"diskRdKB": rdBytes / 1024,
"diskWrKB": wrBytes / 1024,
"netRxKB": rxBytes / 1024,
"netTxKB": txBytes / 1024,
}
for r in ["diskRd", "diskWr", "netRx", "netTx"]:
newStats[r + "Rate"] = self._get_cur_rate(r + "KB")
self._set_max_rate(newStats, r + "Rate")
self.record.insert(0, newStats)
########################
# Libvirt domain class #
########################
class vmmDomainVirtinst(vmmDomain):
"""
Domain object backed by a virtinst Guest object.
Used for launching a details window for customizing a VM before install.
"""
def __init__(self, conn, backend, key):
vmmDomain.__init__(self, conn, backend, key)
self._orig_xml = ""
def get_name(self):
return self._backend.name
def get_id(self):
return -1
def hasSavedImage(self):
return False
def _XMLDesc(self, flags):
raise RuntimeError("Shouldn't be called")
def get_xml(self, *args, **kwargs):
ignore = args
ignore = kwargs
return self._backend.get_install_xml(install=False)
def _refresh_orig_xml(self):
# We need to cache origxml in order to have something to diff against
if not self._orig_xml:
self._orig_xml = self._backend.get_xml_config()
def get_xmlobj(self, inactive=False, refresh_if_nec=True):
self._refresh_orig_xml()
return self._backend
def _reparse_xml(self, *args, **kwargs):
ignore = args
ignore = kwargs
def _define(self, newxml):
ignore = newxml
self._orig_xml = ""
self.emit("config-changed")
def _redefine_xml(self, newxml):
return self._redefine_helper(self._orig_xml, newxml)
def refresh_xml(self, forcesignal=False):
# No caching, so no refresh needed
return
def snapshots_supported(self):
return False
def get_autostart(self):
return self._backend.autostart
def set_autostart(self, val):
self._backend.autostart = bool(val)
self.emit("config-changed")
def define_name(self, newname):
def change(guest):
guest.name = str(newname)
return self._redefine(change)