core-admin/dom0/qmemman/qmemman.py
Rafal Wojtczuk 5a33ed71ce qmemman: use the fact that balloon driver retries
Apparently even if there is not enough xen memory to balloon up,
balloon driver will try to fulfill the request later, when
some memory is freed. Thus, in do_balloon, do not limit mem_set
to the available memory.
2010-09-09 10:36:13 +02:00

157 lines
5.7 KiB
Python
Executable File

import xen.lowlevel.xc
import xen.lowlevel.xs
import string
import time
import qmemman_algo
import os
class DomainState:
def __init__(self, id):
self.meminfo = None
self.memory_actual = None
self.mem_used = None
self.id = id
self.meminfo_updated = False
self.last_target = 0
class SystemState:
def __init__(self):
self.domdict = {}
self.xc = xen.lowlevel.xc.xc()
self.xs = xen.lowlevel.xs.xs()
self.BALOON_DELAY = 0.1
def add_domain(self, id):
self.domdict[id] = DomainState(id)
def del_domain(self, id):
self.domdict.pop(id)
def get_free_xen_memory(self):
return self.xc.physinfo()['free_memory']*1024
# hosts = self.xend_session.session.xenapi.host.get_all()
# host_record = self.xend_session.session.xenapi.host.get_record(hosts[0])
# host_metrics_record = self.xend_session.session.xenapi.host_metrics.get_record(host_record["metrics"])
# ret = host_metrics_record["memory_free"]
# return long(ret)
def refresh_memactual(self):
for domain in self.xc.domain_getinfo():
id = str(domain['domid'])
if self.domdict.has_key(id):
self.domdict[id].memory_actual = domain['mem_kb']*1024
def parse_meminfo(self, meminfo):
dict = {}
l1 = string.split(meminfo,"\n")
for i in l1:
l2 = string.split(i)
if len(l2) >= 2:
dict[string.rstrip(l2[0], ":")] = l2[1]
try:
for i in ('MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree'):
val = int(dict[i])*1024
if (val < 0):
return None
dict[i] = val
except:
return None
if dict['SwapTotal'] < dict['SwapFree']:
return None
return dict
#the below works (and is fast), but then 'xm list' shows unchanged memory value
def mem_set(self, id, val):
print 'mem-set domain', id, 'to', val
self.domdict[id].last_target = val
self.xs.write('', '/local/domain/' + id + '/memory/target', str(val/1024))
#can happen in the middle of domain shutdown
#apparently xc.lowlevel throws exceptions too
try:
self.xc.domain_set_target_mem(int(id), val/1024)
except:
pass
def mem_set_obsolete(self, id, val):
uuid = self.domdict[id].uuid
if val >= 2**31:
print 'limiting memory from ', val, 'to maxint because of xml-rpc lameness'
val = 2**31 - 1
print 'mem-set domain', id, 'to', val
try:
self.xend_session.session.xenapi.VM.set_memory_dynamic_max_live(uuid, val)
self.xend_session.session.xenapi.VM.set_memory_dynamic_min_live(uuid, val)
#can happen in the middle of domain shutdown
except XenAPI.Failure:
pass
def do_balloon(self, memsize):
MAX_TRIES = 20
niter = 0
prev_memory_actual = None
for i in self.domdict.keys():
self.domdict[i].no_progress = False
while True:
xenfree = self.get_free_xen_memory()
print 'got xenfree=', xenfree
if xenfree >= memsize:
return True
self.refresh_memactual()
if prev_memory_actual is not None:
for i in prev_memory_actual.keys():
if prev_memory_actual[i] == self.domdict[i].memory_actual:
self.domdict[i].no_progress = True
print 'domain', i, 'stuck at', self.domdict[i].memory_actual
memset_reqs = qmemman_algo.balloon(memsize-xenfree, self.domdict)
print 'requests:', memset_reqs
if niter > MAX_TRIES or len(memset_reqs) == 0:
return False
prev_memory_actual = {}
for i in memset_reqs:
dom, mem = i
self.mem_set(dom, mem)
prev_memory_actual[dom] = self.domdict[dom].memory_actual
time.sleep(self.BALOON_DELAY)
niter = niter + 1
def refresh_meminfo(self, domid, val):
self.domdict[domid].meminfo = self.parse_meminfo(val)
self.domdict[domid].meminfo_updated = True
def is_balance_req_significant(self, memset_reqs):
total_memory_transfer = 0
MIN_TOTAL_MEMORY_TRANSFER = 150*1024*1024
for rq in memset_reqs:
dom, mem = rq
memory_change = mem - self.domdict[dom].last_target
total_memory_transfer += abs(memory_change)
return total_memory_transfer > MIN_TOTAL_MEMORY_TRANSFER
def print_stats(self, xenfree, memset_reqs):
for i in self.domdict.keys():
if self.domdict[i].meminfo is not None:
print 'dom' , i, 'act/pref', self.domdict[i].memory_actual, qmemman_algo.prefmem(self.domdict[i])
print 'xenfree=', xenfree, 'balance req:', memset_reqs
def do_balance(self):
if os.path.isfile('/var/run/qubes/do-not-membalance'):
return
self.refresh_memactual()
xenfree = self.get_free_xen_memory()
memset_reqs = qmemman_algo.balance(xenfree, self.domdict)
if not self.is_balance_req_significant(memset_reqs):
return
self.print_stats(xenfree, memset_reqs)
for rq in memset_reqs:
dom, mem = rq
self.mem_set(dom, mem)
# for i in self.domdict.keys():
# print 'domain ', i, ' meminfo=', self.domdict[i].meminfo, 'actual mem', self.domdict[i].memory_actual
# print 'domain ', i, 'actual mem', self.domdict[i].memory_actual
# print 'xen free mem', self.get_free_xen_memory()