Files
virt-manager/src/virtManager/domain.py

458 lines
14 KiB
Python

#
# Copyright (C) 2006 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import gobject
import libvirt
import libxml2
import os
import sys
import logging
class vmmDomain(gobject.GObject):
__gsignals__ = {
"status-changed": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
[int]),
"resources-sampled": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
[]),
}
def __init__(self, config, connection, vm, uuid):
self.__gobject_init__()
self.config = config
self.connection = connection
self.vm = vm
self.uuid = uuid
self.lastStatus = None
self.record = []
def set_handle(self, vm):
self.vm = vm
def is_active(self):
if self.vm.ID() == -1:
return False
else:
return True
def get_connection(self):
return self.connection
def get_id(self):
return self.vm.ID()
def get_id_pretty(self):
id = self.get_id()
if id < 0:
return "-"
return str(id)
def get_name(self):
return self.vm.name()
def get_uuid(self):
return self.uuid
def is_read_only(self):
if self.connection.is_read_only():
return True
if self.is_management_domain():
return True
return False
def is_management_domain(self):
if self.vm.ID() == 0:
return True
return False
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 _update_status(self, status=None):
if status == None:
info = self.vm.info()
status = info[0]
status = self._normalize_status(status)
if status != self.lastStatus:
self.lastStatus = status
self.emit("status-changed", status)
def tick(self, now):
hostInfo = self.connection.get_host_info()
info = self.vm.info()
expected = self.config.get_stats_history_length()
current = len(self.record)
if current > expected:
del self.record[expected:current]
prevCpuTime = 0
prevTimestamp = 0
if len(self.record) > 0:
prevTimestamp = self.record[0]["timestamp"]
prevCpuTime = self.record[0]["cpuTimeAbs"]
cpuTime = 0
cpuTimeAbs = 0
pcentCpuTime = 0
if not(info[0] in [libvirt.VIR_DOMAIN_SHUTOFF, libvirt.VIR_DOMAIN_CRASHED]):
cpuTime = info[4] - prevCpuTime
cpuTimeAbs = info[4]
pcentCpuTime = (cpuTime) * 100.0 / ((now - prevTimestamp)*1000.0*1000.0*1000.0*self.connection.host_active_processor_count())
# Due to timing diffs between getting wall time & getting
# the domain's time, its possible to go a tiny bit over
# 100% utilization. This freaks out users of the data, so
# we hard limit it.
if pcentCpuTime > 100.0:
pcentCpuTime = 100.0
# Enforce >= 0 just in case
if pcentCpuTime < 0.0:
pcentCpuTime = 0.0
pcentCurrMem = info[2] * 100.0 / self.connection.host_memory_size()
pcentMaxMem = info[1] * 100.0 / self.connection.host_memory_size()
newStats = { "timestamp": now,
"cpuTime": cpuTime,
"cpuTimeAbs": cpuTimeAbs,
"cpuTimePercent": pcentCpuTime,
"currMem": info[2],
"currMemPercent": pcentCurrMem,
"vcpuCount": info[3],
"maxMem": info[1],
"maxMemPercent": pcentMaxMem,
}
self.record.insert(0, newStats)
nSamples = 5
#nSamples = len(self.record)
if nSamples > len(self.record):
nSamples = len(self.record)
startCpuTime = self.record[nSamples-1]["cpuTimeAbs"]
startTimestamp = self.record[nSamples-1]["timestamp"]
if startTimestamp == now:
self.record[0]["cpuTimeMovingAvg"] = self.record[0]["cpuTimeAbs"]
self.record[0]["cpuTimeMovingAvgPercent"] = 0
else:
self.record[0]["cpuTimeMovingAvg"] = (self.record[0]["cpuTimeAbs"]-startCpuTime) / nSamples
self.record[0]["cpuTimeMovingAvgPercent"] = (self.record[0]["cpuTimeAbs"]-startCpuTime) * 100.0 / ((now-startTimestamp)*1000.0*1000.0*1000.0 * self.connection.host_active_processor_count())
self._update_status(info[0])
self.emit("resources-sampled")
def current_memory(self):
if len(self.record) == 0:
return 0
return self.record[0]["currMem"]
def current_memory_percentage(self):
if len(self.record) == 0:
return 0
return self.record[0]["currMemPercent"]
def current_memory_pretty(self):
mem = self.current_memory()
if mem > (1024*1024):
return "%2.2f GB" % (mem/(1024.0*1024.0))
else:
return "%2.2f MB" % (mem/1024.0)
def maximum_memory(self):
if len(self.record) == 0:
return 0
return self.record[0]["maxMem"]
def maximum_memory_percentage(self):
if len(self.record) == 0:
return 0
return self.record[0]["maxMemPercent"]
def cpu_time(self):
if len(self.record) == 0:
return 0
return self.record[0]["cpuTime"]
def cpu_time_percentage(self):
if len(self.record) == 0:
return 0
return self.record[0]["cpuTimePercent"]
def cpu_time_pretty(self):
return "%2.2f %%" % self.cpu_time_percentage()
def network_traffic(self):
return 1
def network_traffic_percentage(self):
return 1
def disk_usage(self):
return 1
def disk_usage_percentage(self):
return 1
def vcpu_count(self):
if len(self.record) == 0:
return 0
return self.record[0]["vcpuCount"]
def vcpu_max_count(self):
cpus = self.get_xml_string("/domain/vcpu")
return int(cpus)
def cpu_time_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
if i < len(stats):
vector.append(stats[i]["cpuTimePercent"]/100.0)
else:
vector.append(0)
return vector
def cpu_time_vector_limit(self, limit):
cpudata = self.cpu_time_vector()
if len(cpudata) > limit:
cpudata = cpudata[0:limit]
return cpudata
def cpu_time_moving_avg_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
if i < len(stats):
vector.append(stats[i]["cpuTimeMovingAvgPercent"]/100.0)
else:
vector.append(0)
return vector
def current_memory_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
if i < len(stats):
vector.append(stats[i]["currMemPercent"]/100.0)
else:
vector.append(0)
return vector
def network_traffic_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
vector.append(0)
return vector
def disk_usage_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
vector.append(0)
return vector
def shutdown(self):
self.vm.shutdown()
self._update_status()
def startup(self):
self.vm.create()
self._update_status()
def suspend(self):
self.vm.suspend()
self._update_status()
def delete(self):
self.vm.undefine()
def resume(self):
self.vm.resume()
self._update_status()
def save(self, file):
self.vm.save(file)
self._update_status()
def status(self):
return self.lastStatus
def run_status(self):
if self.lastStatus == libvirt.VIR_DOMAIN_RUNNING:
return _("Running")
elif self.lastStatus == libvirt.VIR_DOMAIN_PAUSED:
return _("Paused")
elif self.lastStatus == libvirt.VIR_DOMAIN_SHUTDOWN:
return _("Shutdown")
elif self.lastStatus == libvirt.VIR_DOMAIN_SHUTOFF:
return _("Shutoff")
elif self.lastStatus == libvirt.VIR_DOMAIN_CRASHED:
return _("Crashed")
else:
raise _("Unknown status code")
def run_status_icon(self):
return self.config.get_vm_status_icon(self.status())
def get_xml_string(self, path):
xml = self.vm.XMLDesc(0)
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return None
ctx = doc.xpathNewContext()
try:
ret = ctx.xpathEval(path)
tty = None
if len(ret) == 1:
tty = ret[0].content
ctx.xpathFreeContext()
doc.freeDoc()
return tty
except:
ctx.xpathFreeContext()
doc.freeDoc()
return None
def get_serial_console_tty(self):
return self.get_xml_string("/domain/devices/console/@tty")
def is_serial_console_tty_accessible(self):
tty = self.get_serial_console_tty()
if tty == None:
return False
return os.access(tty, os.R_OK | os.W_OK)
def get_graphics_console(self):
type = self.get_xml_string("/domain/devices/graphics/@type")
port = None
if type == "vnc":
port = self.get_xml_string("/domain/devices/graphics[@type='vnc']/@port")
if port == None:
port = 5900 + self.get_id()
else:
port = int(port)
return [type, "localhost", port]
return [type, None, None]
def get_disk_devices(self):
xml = self.vm.XMLDesc(0)
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return []
ctx = doc.xpathNewContext()
disks = []
try:
ret = ctx.xpathEval("/domain/devices/disk")
for node in ret:
type = node.prop("type")
srcpath = None
devdst = None
for child in node.children:
if child.name == "source":
if type == "file":
srcpath = child.prop("file")
elif type == "block":
srcpath = child.prop("dev")
elif child.name == "target":
devdst = child.prop("dev")
if srcpath == None:
raise "missing source path"
if devdst == None:
raise "missing destination device"
devtype = node.prop("device")
if devtype == None:
devtype = "disk"
disks.append([type, srcpath, devtype, devdst])
finally:
if ctx != None:
ctx.xpathFreeContext()
if doc != None:
doc.freeDoc()
return disks
def get_network_devices(self):
xml = self.vm.XMLDesc(0)
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return []
ctx = doc.xpathNewContext()
disks = []
try:
ret = ctx.xpathEval("/domain/devices/interface")
for node in ret:
type = node.prop("type")
devmac = None
source = None
for child in node.children:
if child.name == "source":
if type == "bridge":
source = child.prop("bridge")
elif child.name == "mac":
devmac = child.prop("address")
if source == None:
source = "-"
devdst = "eth%d" % len(disks)
disks.append([type, source, devdst, devmac])
finally:
if ctx != None:
ctx.xpathFreeContext()
if doc != None:
doc.freeDoc()
return disks
def set_vcpu_count(self, vcpus):
vcpus = int(vcpus)
self.vm.setVcpus(vcpus)
def set_memory(self, memory):
memory = int(memory)
if (memory > self.maximum_memory()):
logging.warning("Requested memory " + str(memory) + " over maximum " + str(self.maximum_memory()))
memory = self.maximum_memory()
self.vm.setMemory(memory)
def set_max_memory(self, memory):
memory = int(memory)
self.vm.setMaxMemory(memory)
gobject.type_register(vmmDomain)