mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-22 22:03:58 +03:00
statsmanager: Absorb more domain stats handling
This commit is contained in:
parent
f553cb67c2
commit
a88a28d0b8
@ -211,14 +211,6 @@ class vmmDomain(vmmLibvirtObject):
|
||||
|
||||
self.cloning = False
|
||||
|
||||
self.stats = []
|
||||
self._stats_rates = {
|
||||
"diskRdRate": 10.0,
|
||||
"diskWrRate": 10.0,
|
||||
"netTxRate": 10.0,
|
||||
"netRxRate": 10.0,
|
||||
}
|
||||
|
||||
self._install_abort = False
|
||||
self._id = None
|
||||
self._uuid = None
|
||||
@ -232,10 +224,6 @@ class vmmDomain(vmmLibvirtObject):
|
||||
self.managedsave_supported = False
|
||||
self._domain_state_supported = False
|
||||
|
||||
self.stats_net_skip = []
|
||||
self.stats_disk_skip = []
|
||||
self.summary_disk_stats_skip = False
|
||||
|
||||
self.inspection = vmmInspectionData()
|
||||
|
||||
def _cleanup(self):
|
||||
@ -258,7 +246,8 @@ class vmmDomain(vmmLibvirtObject):
|
||||
# Prime caches
|
||||
info = self._backend.info()
|
||||
self._refresh_status(newstatus=info[0])
|
||||
self._tick_stats()
|
||||
# XXX _tick_stats
|
||||
# self._tick_stats()
|
||||
self.has_managed_save()
|
||||
self.snapshots_supported()
|
||||
|
||||
@ -1512,95 +1501,52 @@ class vmmDomain(vmmLibvirtObject):
|
||||
# Don't schedule any conn update, migrate dialog handles it for us
|
||||
|
||||
|
||||
#################
|
||||
# Stats helpers #
|
||||
#################
|
||||
|
||||
def _get_cur_rate(self, what):
|
||||
if len(self.stats) > 1:
|
||||
ret = (float(self.stats[0][what] -
|
||||
self.stats[1][what]) /
|
||||
float(self.stats[0]["timestamp"] -
|
||||
self.stats[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._stats_rates[what]:
|
||||
self._stats_rates[what] = record[what]
|
||||
def _get_max_rate(self, name1, name2):
|
||||
return float(max(self._stats_rates[name1], self._stats_rates[name2]))
|
||||
|
||||
def _get_record_helper(self, record_name):
|
||||
if len(self.stats) == 0:
|
||||
return 0
|
||||
return self.stats[0][record_name]
|
||||
|
||||
def _vector_helper(self, record_name, limit, ceil=100.0):
|
||||
vector = []
|
||||
statslen = self.config.get_stats_history_length() + 1
|
||||
if limit is not None:
|
||||
statslen = min(statslen, limit)
|
||||
|
||||
for i in range(statslen):
|
||||
if i < len(self.stats):
|
||||
vector.append(self.stats[i][record_name] / ceil)
|
||||
else:
|
||||
vector.append(0)
|
||||
|
||||
return vector
|
||||
|
||||
def _in_out_vector_helper(self, name1, name2, limit, ceil):
|
||||
if ceil is None:
|
||||
ceil = self._get_max_rate(name1, name2)
|
||||
|
||||
return (self._vector_helper(name1, limit, ceil=ceil),
|
||||
self._vector_helper(name2, limit, ceil=ceil))
|
||||
|
||||
|
||||
###################
|
||||
# Stats accessors #
|
||||
###################
|
||||
|
||||
def _get_stats(self):
|
||||
return self.conn.statsmanager.get_vm_statslist(self)
|
||||
def stats_memory(self):
|
||||
return self._get_record_helper("curmem")
|
||||
return self._get_stats().get_record("curmem")
|
||||
def cpu_time(self):
|
||||
return self._get_record_helper("cpuTime")
|
||||
return self._get_stats().get_record("cpuTime")
|
||||
def host_cpu_time_percentage(self):
|
||||
return self._get_record_helper("cpuHostPercent")
|
||||
return self._get_stats().get_record("cpuHostPercent")
|
||||
def guest_cpu_time_percentage(self):
|
||||
return self._get_record_helper("cpuGuestPercent")
|
||||
return self._get_stats().get_record("cpuGuestPercent")
|
||||
def network_rx_rate(self):
|
||||
return self._get_record_helper("netRxRate")
|
||||
return self._get_stats().get_record("netRxRate")
|
||||
def network_tx_rate(self):
|
||||
return self._get_record_helper("netTxRate")
|
||||
return self._get_stats().get_record("netTxRate")
|
||||
def disk_read_rate(self):
|
||||
return self._get_record_helper("diskRdRate")
|
||||
return self._get_stats().get_record("diskRdRate")
|
||||
def disk_write_rate(self):
|
||||
return self._get_record_helper("diskWrRate")
|
||||
return self._get_stats().get_record("diskWrRate")
|
||||
|
||||
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")
|
||||
stats = self._get_stats()
|
||||
return max(stats.netRxMaxRate, stats.netTxMaxRate)
|
||||
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")
|
||||
stats = self._get_stats()
|
||||
return max(stats.diskRdMaxRate, stats.diskWrMaxRate)
|
||||
|
||||
def host_cpu_time_vector(self, limit=None):
|
||||
return self._vector_helper("cpuHostPercent", limit)
|
||||
return self._get_stats().get_vector("cpuHostPercent", limit)
|
||||
def guest_cpu_time_vector(self, limit=None):
|
||||
return self._vector_helper("cpuGuestPercent", limit)
|
||||
return self._get_stats().get_vector("cpuGuestPercent", limit)
|
||||
def stats_memory_vector(self, limit=None):
|
||||
return self._vector_helper("currMemPercent", limit)
|
||||
return self._get_stats().get_vector("currMemPercent", limit)
|
||||
def network_traffic_vectors(self, limit=None, ceil=None):
|
||||
return self._in_out_vector_helper(
|
||||
"netRxRate", "netTxRate", limit, ceil)
|
||||
return self._get_stats().get_in_out_vector(
|
||||
"netRxRate", "netTxRate", limit, ceil)
|
||||
def disk_io_vectors(self, limit=None, ceil=None):
|
||||
return self._in_out_vector_helper(
|
||||
"diskRdRate", "diskWrRate", limit, ceil)
|
||||
return self._get_stats().get_in_out_vector(
|
||||
"diskRdRate", "diskWrRate", limit, ceil)
|
||||
|
||||
|
||||
###################
|
||||
@ -1727,26 +1673,12 @@ class vmmDomain(vmmLibvirtObject):
|
||||
dosignal = self._refresh_status(newstatus=info[0], cansignal=False)
|
||||
|
||||
if stats_update:
|
||||
self._tick_stats()
|
||||
self.conn.statsmanager.refresh_vm_stats(self)
|
||||
if dosignal:
|
||||
self.idle_emit("state-changed")
|
||||
if stats_update:
|
||||
self.idle_emit("resources-sampled")
|
||||
|
||||
def _tick_stats(self):
|
||||
expected = self.config.get_stats_history_length()
|
||||
current = len(self.stats)
|
||||
if current > expected:
|
||||
del self.stats[expected:current]
|
||||
|
||||
newStats = self.conn.statsmanager.refresh_vm_stats(self)
|
||||
|
||||
for r in ["diskRd", "diskWr", "netRx", "netTx"]:
|
||||
newStats[r + "Rate"] = self._get_cur_rate(r + "KiB")
|
||||
self._set_max_rate(newStats, r + "Rate")
|
||||
|
||||
self.stats.insert(0, newStats)
|
||||
|
||||
|
||||
########################
|
||||
# Libvirt domain class #
|
||||
|
@ -14,12 +14,115 @@ from virtinst import util
|
||||
from .baseclass import vmmGObject
|
||||
|
||||
|
||||
class _VMStatsRecord(object):
|
||||
"""
|
||||
Tracks a set of VM stats for a single timestamp
|
||||
"""
|
||||
def __init__(self, timestamp,
|
||||
cpuTime, cpuTimeAbs,
|
||||
cpuHostPercent, cpuGuestPercent,
|
||||
curmem, currMemPercent,
|
||||
diskRdBytes, diskWrBytes,
|
||||
netRxBytes, netTxBytes):
|
||||
self.timestamp = timestamp
|
||||
self.cpuTime = cpuTime
|
||||
self.cpuTimeAbs = cpuTimeAbs
|
||||
self.cpuHostPercent = cpuHostPercent
|
||||
self.cpuGuestPercent = cpuGuestPercent
|
||||
self.curmem = curmem
|
||||
self.currMemPercent = currMemPercent
|
||||
self.diskRdKiB = diskRdBytes // 1024
|
||||
self.diskWrKiB = diskWrBytes // 1024
|
||||
self.netRxKiB = netRxBytes // 1024
|
||||
self.netTxKiB = netTxBytes // 1024
|
||||
|
||||
# These are set in _VMStatsList.append_stats
|
||||
self.diskRdRate = None
|
||||
self.diskWrRate = None
|
||||
self.netRxRate = None
|
||||
self.netTxRate = None
|
||||
|
||||
|
||||
class _VMStatsList(vmmGObject):
|
||||
"""
|
||||
Tracks a list of VMStatsRecords for a single VM
|
||||
"""
|
||||
def __init__(self):
|
||||
vmmGObject.__init__(self)
|
||||
self._stats = []
|
||||
|
||||
self.diskRdMaxRate = 10.0
|
||||
self.diskWrMaxRate = 10.0
|
||||
self.netRxMaxRate = 10.0
|
||||
self.netTxMaxRate = 10.0
|
||||
|
||||
self.mem_stats_period_is_set = False
|
||||
self.stats_disk_skip = []
|
||||
self.stats_net_skip = []
|
||||
|
||||
def _cleanup(self):
|
||||
pass
|
||||
|
||||
def append_stats(self, newstats):
|
||||
expected = self.config.get_stats_history_length()
|
||||
current = len(self._stats)
|
||||
if current > expected:
|
||||
del(self._stats[expected:current])
|
||||
|
||||
def _calculate_rate(record_name):
|
||||
ret = 0.0
|
||||
if self._stats:
|
||||
oldstats = self._stats[0]
|
||||
ratediff = (getattr(newstats, record_name) -
|
||||
getattr(oldstats, record_name))
|
||||
timediff = newstats.timestamp - oldstats.timestamp
|
||||
ret = float(ratediff) / float(timediff)
|
||||
return max(ret, 0.0)
|
||||
|
||||
newstats.diskRdRate = _calculate_rate("diskRdKiB")
|
||||
newstats.diskWrRate = _calculate_rate("diskWrKiB")
|
||||
newstats.netRxRate = _calculate_rate("netRxKiB")
|
||||
newstats.netTxRate = _calculate_rate("netTxKiB")
|
||||
|
||||
self.diskRdMaxRate = max(newstats.diskRdRate, self.diskRdMaxRate)
|
||||
self.diskWrMaxRate = max(newstats.diskWrRate, self.diskWrMaxRate)
|
||||
self.netRxMaxRate = max(newstats.netRxRate, self.netRxMaxRate)
|
||||
self.netTxMaxRate = max(newstats.netTxRate, self.netTxMaxRate)
|
||||
|
||||
self._stats.insert(0, newstats)
|
||||
|
||||
def get_record(self, record_name):
|
||||
if not self._stats:
|
||||
return 0
|
||||
return getattr(self._stats[0], record_name)
|
||||
|
||||
def get_vector(self, record_name, limit, ceil=100.0):
|
||||
vector = []
|
||||
statslen = self.config.get_stats_history_length() + 1
|
||||
if limit is not None:
|
||||
statslen = min(statslen, limit)
|
||||
|
||||
for i in range(statslen):
|
||||
if i < len(self._stats):
|
||||
vector.append(getattr(self._stats[i], record_name) / ceil)
|
||||
else:
|
||||
vector.append(0)
|
||||
return vector
|
||||
|
||||
def get_in_out_vector(self, name1, name2, limit, ceil):
|
||||
if ceil is None:
|
||||
ceil = max(self.get_record(name1), self.get_record(name2), 10.0)
|
||||
return (self.get_vector(name1, limit, ceil=ceil),
|
||||
self.get_vector(name2, limit, ceil=ceil))
|
||||
|
||||
|
||||
class vmmStatsManager(vmmGObject):
|
||||
"""
|
||||
Class for polling statistics
|
||||
"""
|
||||
def __init__(self):
|
||||
vmmGObject.__init__(self)
|
||||
self._vm_stats = {}
|
||||
self._latest_all_stats = []
|
||||
self._all_stats_supported = True
|
||||
self._enable_mem_stats = False
|
||||
@ -31,7 +134,6 @@ class vmmStatsManager(vmmGObject):
|
||||
self._disk_stats_supported = True
|
||||
self._disk_stats_lxc_supported = True
|
||||
self._mem_stats_supported = True
|
||||
self._mem_stats_period_is_set = False
|
||||
|
||||
self._on_config_sample_network_traffic_changed()
|
||||
self._on_config_sample_disk_io_changed()
|
||||
@ -80,15 +182,11 @@ class vmmStatsManager(vmmGObject):
|
||||
if not self._enable_cpu_stats:
|
||||
return 0, 0, 0, 0
|
||||
|
||||
prevCpuTime = 0
|
||||
prevTimestamp = 0
|
||||
cpuTime = 0
|
||||
pcentHostCpu = 0
|
||||
pcentGuestCpu = 0
|
||||
|
||||
if len(vm.stats) > 0:
|
||||
prevTimestamp = vm.stats[0]["timestamp"]
|
||||
prevCpuTime = vm.stats[0]["cpuTimeAbs"]
|
||||
cpuHostPercent = 0
|
||||
cpuGuestPercent = 0
|
||||
prevTimestamp = self.get_vm_statslist(vm).get_record("timestamp")
|
||||
prevCpuTime = self.get_vm_statslist(vm).get_record("cpuTimeAbs")
|
||||
|
||||
if allstats:
|
||||
state = allstats.get("state.state", 0)
|
||||
@ -109,15 +207,15 @@ class vmmStatsManager(vmmGObject):
|
||||
|
||||
pcentbase = (((cpuTime) * 100.0) /
|
||||
((now - prevTimestamp) * 1000.0 * 1000.0 * 1000.0))
|
||||
pcentHostCpu = pcentbase / hostcpus
|
||||
cpuHostPercent = pcentbase / hostcpus
|
||||
# Under RHEL-5.9 using a XEN HV guestcpus can be 0 during shutdown
|
||||
# so play safe and check it.
|
||||
pcentGuestCpu = guestcpus > 0 and pcentbase / guestcpus or 0
|
||||
cpuGuestPercent = guestcpus > 0 and pcentbase / guestcpus or 0
|
||||
|
||||
pcentHostCpu = max(0.0, min(100.0, pcentHostCpu))
|
||||
pcentGuestCpu = max(0.0, min(100.0, pcentGuestCpu))
|
||||
cpuHostPercent = max(0.0, min(100.0, cpuHostPercent))
|
||||
cpuGuestPercent = max(0.0, min(100.0, cpuGuestPercent))
|
||||
|
||||
return cpuTime, cpuTimeAbs, pcentHostCpu, pcentGuestCpu
|
||||
return cpuTime, cpuTimeAbs, cpuHostPercent, cpuGuestPercent
|
||||
|
||||
|
||||
######################
|
||||
@ -125,6 +223,7 @@ class vmmStatsManager(vmmGObject):
|
||||
######################
|
||||
|
||||
def _old_net_stats_helper(self, vm, dev):
|
||||
statslist = self.get_vm_statslist(vm)
|
||||
try:
|
||||
io = vm.get_backend().interfaceStats(dev)
|
||||
if io:
|
||||
@ -141,7 +240,7 @@ class vmmStatsManager(vmmGObject):
|
||||
vm.get_name(), dev, err)
|
||||
if vm.is_active():
|
||||
logging.debug("Adding %s to skip list", dev)
|
||||
vm.stats_net_skip.append(dev)
|
||||
statslist.stats_net_skip.append(dev)
|
||||
else:
|
||||
logging.debug("Aren't running, don't add to skiplist")
|
||||
|
||||
@ -150,10 +249,11 @@ class vmmStatsManager(vmmGObject):
|
||||
def _sample_net_stats(self, vm, allstats):
|
||||
rx = 0
|
||||
tx = 0
|
||||
statslist = self.get_vm_statslist(vm)
|
||||
if (not self._net_stats_supported or
|
||||
not self._enable_net_stats or
|
||||
not vm.is_active()):
|
||||
vm.stats_net_skip = []
|
||||
statslist.stats_net_skip = []
|
||||
return rx, tx
|
||||
|
||||
if allstats:
|
||||
@ -168,7 +268,7 @@ class vmmStatsManager(vmmGObject):
|
||||
dev = iface.target_dev
|
||||
if not dev:
|
||||
continue
|
||||
if dev in vm.stats_net_skip:
|
||||
if dev in statslist.stats_net_skip:
|
||||
continue
|
||||
|
||||
devrx, devtx = self._old_net_stats_helper(vm, dev)
|
||||
@ -183,6 +283,7 @@ class vmmStatsManager(vmmGObject):
|
||||
#######################
|
||||
|
||||
def _old_disk_stats_helper(self, vm, dev):
|
||||
statslist = self.get_vm_statslist(vm)
|
||||
try:
|
||||
io = vm.get_backend().blockStats(dev)
|
||||
if io:
|
||||
@ -199,7 +300,7 @@ class vmmStatsManager(vmmGObject):
|
||||
vm.get_name(), dev, err)
|
||||
if vm.is_active():
|
||||
logging.debug("Adding %s to skip list", dev)
|
||||
vm.stats_disk_skip.append(dev)
|
||||
statslist.stats_disk_skip.append(dev)
|
||||
else:
|
||||
logging.debug("Aren't running, don't add to skiplist")
|
||||
|
||||
@ -208,10 +309,11 @@ class vmmStatsManager(vmmGObject):
|
||||
def _sample_disk_stats(self, vm, allstats):
|
||||
rd = 0
|
||||
wr = 0
|
||||
statslist = self.get_vm_statslist(vm)
|
||||
if (not self._disk_stats_supported or
|
||||
not self._enable_disk_stats or
|
||||
not vm.is_active()):
|
||||
vm.stats_disk_skip = []
|
||||
statslist.stats_disk_skip = []
|
||||
return rd, wr
|
||||
|
||||
if allstats:
|
||||
@ -238,7 +340,7 @@ class vmmStatsManager(vmmGObject):
|
||||
dev = disk.target
|
||||
if not dev:
|
||||
continue
|
||||
if dev in vm.stats_disk_skip:
|
||||
if dev in statslist.stats_disk_skip:
|
||||
continue
|
||||
|
||||
diskrd, diskwr = self._old_disk_stats_helper(vm, dev)
|
||||
@ -288,15 +390,16 @@ class vmmStatsManager(vmmGObject):
|
||||
return totalmem, curmem
|
||||
|
||||
def _sample_mem_stats(self, vm, allstats):
|
||||
statslist = self.get_vm_statslist(vm)
|
||||
if (not self._mem_stats_supported or
|
||||
not self._enable_mem_stats or
|
||||
not vm.is_active()):
|
||||
self._mem_stats_period_is_set = False
|
||||
statslist.mem_stats_period_is_set = False
|
||||
return 0, 0
|
||||
|
||||
if self._mem_stats_period_is_set is False:
|
||||
if statslist.mem_stats_period_is_set is False:
|
||||
self._set_mem_stats_period(vm)
|
||||
self._mem_stats_period_is_set = True
|
||||
statslist.mem_stats_period_is_set = True
|
||||
|
||||
if allstats:
|
||||
totalmem = allstats.get("balloon.current", 1)
|
||||
@ -305,15 +408,15 @@ class vmmStatsManager(vmmGObject):
|
||||
else:
|
||||
totalmem, curmem = self._old_mem_stats_helper(vm)
|
||||
|
||||
pcentCurrMem = (curmem / float(totalmem)) * 100
|
||||
pcentCurrMem = max(0.0, min(pcentCurrMem, 100.0))
|
||||
currMemPercent = (curmem / float(totalmem)) * 100
|
||||
currMemPercent = max(0.0, min(currMemPercent, 100.0))
|
||||
|
||||
return pcentCurrMem, curmem
|
||||
return currMemPercent, curmem
|
||||
|
||||
|
||||
#####################
|
||||
# allstats handling #
|
||||
#####################
|
||||
####################
|
||||
# alltats handling #
|
||||
####################
|
||||
|
||||
def _get_all_stats(self, conn):
|
||||
if not self._all_stats_supported:
|
||||
@ -337,8 +440,13 @@ class vmmStatsManager(vmmGObject):
|
||||
logging.debug("Error call getAllDomainStats(): %s", err)
|
||||
return stats
|
||||
|
||||
|
||||
##############
|
||||
# Public API #
|
||||
##############
|
||||
|
||||
def refresh_vm_stats(self, vm):
|
||||
now = time.time()
|
||||
timestamp = time.time()
|
||||
|
||||
domallstats = None
|
||||
for _domstat in self._latest_all_stats:
|
||||
@ -346,27 +454,24 @@ class vmmStatsManager(vmmGObject):
|
||||
domallstats = _domstat[1]
|
||||
break
|
||||
|
||||
cpuTime, cpuTimeAbs, pcentHostCpu, pcentGuestCpu = \
|
||||
self._sample_cpu_stats(now, vm, domallstats)
|
||||
pcentCurrMem, curmem = self._sample_mem_stats(vm, domallstats)
|
||||
rdBytes, wrBytes = self._sample_disk_stats(vm, domallstats)
|
||||
rxBytes, txBytes = self._sample_net_stats(vm, domallstats)
|
||||
cpuTime, cpuTimeAbs, cpuHostPercent, cpuGuestPercent = \
|
||||
self._sample_cpu_stats(timestamp, vm, domallstats)
|
||||
currMemPercent, curmem = self._sample_mem_stats(vm, domallstats)
|
||||
diskRdBytes, diskWrBytes = self._sample_disk_stats(vm, domallstats)
|
||||
netRxBytes, netTxBytes = self._sample_net_stats(vm, domallstats)
|
||||
|
||||
newStats = {
|
||||
"timestamp": now,
|
||||
"cpuTime": cpuTime,
|
||||
"cpuTimeAbs": cpuTimeAbs,
|
||||
"cpuHostPercent": pcentHostCpu,
|
||||
"cpuGuestPercent": pcentGuestCpu,
|
||||
"curmem": curmem,
|
||||
"currMemPercent": pcentCurrMem,
|
||||
"diskRdKiB": rdBytes // 1024,
|
||||
"diskWrKiB": wrBytes // 1024,
|
||||
"netRxKiB": rxBytes // 1024,
|
||||
"netTxKiB": txBytes // 1024,
|
||||
}
|
||||
|
||||
return newStats
|
||||
newstats = _VMStatsRecord(
|
||||
timestamp, cpuTime, cpuTimeAbs,
|
||||
cpuHostPercent, cpuGuestPercent,
|
||||
curmem, currMemPercent,
|
||||
diskRdBytes, diskWrBytes,
|
||||
netRxBytes, netTxBytes)
|
||||
self.get_vm_statslist(vm).append_stats(newstats)
|
||||
|
||||
def cache_all_stats(self, conn):
|
||||
self._latest_all_stats = self._get_all_stats(conn)
|
||||
|
||||
def get_vm_statslist(self, vm):
|
||||
if vm.get_connkey() not in self._vm_stats:
|
||||
self._vm_stats[vm.get_connkey()] = _VMStatsList()
|
||||
return self._vm_stats[vm.get_connkey()]
|
||||
|
Loading…
x
Reference in New Issue
Block a user