Memory management across VMs, first release
This commit is contained in:
parent
8317c2ca18
commit
62487c0f1e
@ -87,6 +87,7 @@ start()
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
/usr/lib/qubes/meminfo-writer &
|
||||||
[ -x /rw/config/rc.local ] && /rw/config/rc.local
|
[ -x /rw/config/rc.local ] && /rw/config/rc.local
|
||||||
success
|
success
|
||||||
echo ""
|
echo ""
|
||||||
|
4
common/meminfo-writer
Executable file
4
common/meminfo-writer
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
while sleep 1 ; do
|
||||||
|
xenstore-write memory/meminfo "`cat /proc/meminfo`"
|
||||||
|
done
|
@ -56,6 +56,8 @@ start()
|
|||||||
xm mem-set 0 1600
|
xm mem-set 0 1600
|
||||||
cp /var/lib/qubes/qubes.xml /var/lib/qubes/backup/qubes-$(date +%F-%T).xml
|
cp /var/lib/qubes/qubes.xml /var/lib/qubes/backup/qubes-$(date +%F-%T).xml
|
||||||
setup_dvm_files
|
setup_dvm_files
|
||||||
|
/usr/lib/qubes/qmemman_daemon.py >/var/log/qubes/qmemman.log 2>/var/log/qubes/qmemman.errs &
|
||||||
|
/usr/lib/qubes/meminfo-writer &
|
||||||
touch /var/lock/subsys/qubes_core
|
touch /var/lock/subsys/qubes_core
|
||||||
success
|
success
|
||||||
echo
|
echo
|
||||||
|
@ -30,6 +30,7 @@ import time
|
|||||||
from qubes.qubes import QubesVmCollection
|
from qubes.qubes import QubesVmCollection
|
||||||
from qubes.qubes import QubesException
|
from qubes.qubes import QubesException
|
||||||
from qubes.qubes import QubesDaemonPidfile
|
from qubes.qubes import QubesDaemonPidfile
|
||||||
|
from qubes.qmemman_client import QMemmanClient
|
||||||
|
|
||||||
filename_seq = 50
|
filename_seq = 50
|
||||||
pen_cmd = '/usr/lib/qubes/qubes_pencmd'
|
pen_cmd = '/usr/lib/qubes/qubes_pencmd'
|
||||||
@ -187,13 +188,11 @@ class DomainState:
|
|||||||
|
|
||||||
def handle_transfer_disposable(self, transaction_seq):
|
def handle_transfer_disposable(self, transaction_seq):
|
||||||
|
|
||||||
mem_for_dvm = 400
|
qmemman_client = QMemmanClient()
|
||||||
xenfreepages_s = subprocess.Popen(["/usr/lib/qubes/xenfreepages"],stdout=subprocess.PIPE).stdout.readline()
|
if not qmemman_client.request_memory(400*1024*1024):
|
||||||
xenfree_mb = int(xenfreepages_s)*4096/1024/1024
|
qmemman_client.close()
|
||||||
if xenfree_mb < mem_for_dvm:
|
errmsg = 'Not enough memory to create DVM. '
|
||||||
errmsg = 'Not enough memory to create DVM: '
|
errmsg +='Terminate some appVM and retry.'
|
||||||
errmsg +='have ' + str(xenfree_mb) + 'MB, need '
|
|
||||||
errmsg +=str(mem_for_dvm) + 'MB. Terminate some appVM and retry.'
|
|
||||||
subprocess.call(['/usr/bin/kdialog', '--sorry', errmsg])
|
subprocess.call(['/usr/bin/kdialog', '--sorry', errmsg])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -205,12 +204,14 @@ class DomainState:
|
|||||||
if vm is None:
|
if vm is None:
|
||||||
logproc( 'Domain ' + vmname + ' does not exist ?')
|
logproc( 'Domain ' + vmname + ' does not exist ?')
|
||||||
qvm_collection.unlock_db()
|
qvm_collection.unlock_db()
|
||||||
|
qmemman_client.close()
|
||||||
return False
|
return False
|
||||||
retcode = subprocess.call(['/usr/lib/qubes/qubes_restore',
|
retcode = subprocess.call(['/usr/lib/qubes/qubes_restore',
|
||||||
current_savefile,
|
current_savefile,
|
||||||
'-c', vm.label.color,
|
'-c', vm.label.color,
|
||||||
'-i', vm.label.icon,
|
'-i', vm.label.icon,
|
||||||
'-l', str(vm.label.index)])
|
'-l', str(vm.label.index)])
|
||||||
|
qmemman_client.close()
|
||||||
if retcode != 0:
|
if retcode != 0:
|
||||||
subprocess.call(['/usr/bin/kdialog', '--sorry', 'DisposableVM creation failed, see qubes_restore.log'])
|
subprocess.call(['/usr/bin/kdialog', '--sorry', 'DisposableVM creation failed, see qubes_restore.log'])
|
||||||
qvm_collection.unlock_db()
|
qvm_collection.unlock_db()
|
||||||
|
185
dom0/qmemman/qmemman.py
Executable file
185
dom0/qmemman/qmemman.py
Executable file
@ -0,0 +1,185 @@
|
|||||||
|
import xmlrpclib
|
||||||
|
from xen.xm import XenAPI
|
||||||
|
import xen.lowlevel.xc
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
import qmemman_algo
|
||||||
|
import os
|
||||||
|
|
||||||
|
class XendSession(object):
|
||||||
|
def __init__(self):
|
||||||
|
# self.get_xend_session_old_api()
|
||||||
|
self.get_xend_session_new_api()
|
||||||
|
|
||||||
|
# def get_xend_session_old_api(self):
|
||||||
|
# from xen.xend import XendClient
|
||||||
|
# from xen.util.xmlrpcclient import ServerProxy
|
||||||
|
# self.xend_server = ServerProxy(XendClient.uri)
|
||||||
|
# if self.xend_server is None:
|
||||||
|
# print "get_xend_session_old_api(): cannot open session!"
|
||||||
|
|
||||||
|
|
||||||
|
def get_xend_session_new_api(self):
|
||||||
|
xend_socket_uri = "httpu:///var/run/xend/xen-api.sock"
|
||||||
|
self.session = XenAPI.Session (xend_socket_uri)
|
||||||
|
self.session.login_with_password ("", "")
|
||||||
|
if self.session is None:
|
||||||
|
print "get_xend_session_new_api(): cannot open session!"
|
||||||
|
|
||||||
|
class DomainState:
|
||||||
|
def __init__(self, id):
|
||||||
|
self.meminfo = None
|
||||||
|
self.memory_actual = None
|
||||||
|
self.mem_used = None
|
||||||
|
self.uuid = None
|
||||||
|
self.id = id
|
||||||
|
self.meminfo_updated = False
|
||||||
|
|
||||||
|
class SystemState:
|
||||||
|
def __init__(self):
|
||||||
|
self.xend_session = XendSession()
|
||||||
|
self.domdict = {}
|
||||||
|
self.xc = xen.lowlevel.xc.xc()
|
||||||
|
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):
|
||||||
|
update_uuid_info = False
|
||||||
|
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
|
||||||
|
if self.domdict[id].uuid is None:
|
||||||
|
update_uuid_info = True
|
||||||
|
if not update_uuid_info:
|
||||||
|
return
|
||||||
|
dom_recs = self.xend_session.session.xenapi.VM.get_all_records()
|
||||||
|
# dom_metrics_recs = self.xend_session.session.xenapi.VM_metrics.get_all_records()
|
||||||
|
for dom_ref, dom_rec in dom_recs.items():
|
||||||
|
# dom_metrics_rec = dom_metrics_recs[dom_rec['metrics']]
|
||||||
|
id = dom_rec['domid']
|
||||||
|
# mem = int(dom_metrics_rec['memory_actual'])/1024
|
||||||
|
if (self.domdict.has_key(id)):
|
||||||
|
# self.domdict[id].memory_actual = mem
|
||||||
|
self.domdict[id].uuid = dom_rec['uuid']
|
||||||
|
|
||||||
|
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_alternative(self, id, val):
|
||||||
|
os.system('xenstore-write /local/domain/' + id + '/memory/target ' + str(val/1024))
|
||||||
|
self.xc.domain_set_target_mem(int(id), val/1024)
|
||||||
|
|
||||||
|
def mem_set(self, id, val):
|
||||||
|
uuid = self.domdict[id].uuid
|
||||||
|
print 'mem-set domain', id, 'to', val
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 adjust_inflates_to_xenfree(self, reqs, idx):
|
||||||
|
i = idx
|
||||||
|
memory_needed = 0
|
||||||
|
while i < len(reqs):
|
||||||
|
dom, mem = reqs[i]
|
||||||
|
memory_needed += mem - self.domdict[dom].memory_actual
|
||||||
|
i = i + 1
|
||||||
|
scale = 1.0*self.get_free_xen_memory()/memory_needed
|
||||||
|
dom, mem = reqs[idx]
|
||||||
|
scaled_req = self.domdict[dom].memory_actual + scale*(mem - self.domdict[dom].memory_actual)
|
||||||
|
return int(scaled_req)
|
||||||
|
|
||||||
|
def do_balance(self):
|
||||||
|
if os.path.isfile('/etc/do-not-membalance'):
|
||||||
|
return
|
||||||
|
self.refresh_memactual()
|
||||||
|
xenfree = self.get_free_xen_memory()
|
||||||
|
memset_reqs = qmemman_algo.balance(xenfree, self.domdict)
|
||||||
|
wait_before_first_inflate = False
|
||||||
|
i = 0
|
||||||
|
while i < len(memset_reqs):
|
||||||
|
dom, mem = memset_reqs[i]
|
||||||
|
memory_change = mem - self.domdict[dom].memory_actual
|
||||||
|
if abs(memory_change) < 100*1024*1024:
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
if memory_change < 0:
|
||||||
|
wait_before_first_inflate = True
|
||||||
|
else:
|
||||||
|
if wait_before_first_inflate:
|
||||||
|
time.sleep(self.BALOON_DELAY)
|
||||||
|
wait_before_first_inflate = False
|
||||||
|
#the following is called before _each_ inflate, to account for possibility that
|
||||||
|
#previously triggered memory release is in progress
|
||||||
|
mem = self.adjust_inflates_to_xenfree(memset_reqs, i)
|
||||||
|
self.mem_set(dom, mem)
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
# 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()
|
101
dom0/qmemman/qmemman_algo.py
Executable file
101
dom0/qmemman/qmemman_algo.py
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
def is_suspicious(dom):
|
||||||
|
ret = False
|
||||||
|
if dom.meminfo['SwapTotal'] < dom.meminfo['SwapFree']:
|
||||||
|
ret = True
|
||||||
|
if dom.memory_actual < dom.meminfo['MemFree'] + dom.meminfo['Cached'] + dom.meminfo['Buffers']:
|
||||||
|
ret = True
|
||||||
|
if ret:
|
||||||
|
print 'suspicious meminfo for domain', dom.id, 'mem actual', dom.memory_actual, dom.meminfo
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def recalc_mem_used(domdict):
|
||||||
|
for domid in domdict.keys():
|
||||||
|
dom = domdict[domid]
|
||||||
|
if dom.meminfo_updated:
|
||||||
|
dom.meminfo_updated = False
|
||||||
|
if is_suspicious(dom):
|
||||||
|
dom.meminfo = None
|
||||||
|
dom.mem_used = None
|
||||||
|
else:
|
||||||
|
dom.mem_used = dom.memory_actual - dom.meminfo['MemFree'] - dom.meminfo['Cached'] - dom.meminfo['Buffers'] + dom.meminfo['SwapTotal'] - dom.meminfo['SwapFree']
|
||||||
|
|
||||||
|
def prefmem(dom):
|
||||||
|
if dom.meminfo_updated:
|
||||||
|
raise AssertionError('meminfo_updated=True in prefmem')
|
||||||
|
CACHE_FACTOR = 1.3
|
||||||
|
#dom0 is special, as it must have large cache, for vbds. Thus, give it a special boost
|
||||||
|
if dom.id == '0':
|
||||||
|
return dom.mem_used*CACHE_FACTOR + 350*1024*1024
|
||||||
|
return dom.mem_used*CACHE_FACTOR
|
||||||
|
|
||||||
|
def memneeded(dom):
|
||||||
|
#do not change
|
||||||
|
#in balance(), "distribute totalsum proportionally to mempref" relies on this exact formula
|
||||||
|
ret = prefmem(dom) - dom.memory_actual
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def balloon(memsize, domdict):
|
||||||
|
REQ_SAFETY_NET_FACTOR = 1.05
|
||||||
|
donors = list()
|
||||||
|
request = list()
|
||||||
|
available = 0
|
||||||
|
recalc_mem_used(domdict)
|
||||||
|
for i in domdict.keys():
|
||||||
|
if domdict[i].meminfo is None:
|
||||||
|
continue
|
||||||
|
if domdict[i].no_progress:
|
||||||
|
continue
|
||||||
|
need = memneeded(domdict[i])
|
||||||
|
if need < 0:
|
||||||
|
print 'balloon: dom' , i, 'has actual memory', domdict[i].memory_actual
|
||||||
|
donors.append((i,-need))
|
||||||
|
available-=need
|
||||||
|
print 'req=', memsize, 'avail=', available, 'donors', donors
|
||||||
|
if available<memsize:
|
||||||
|
return ()
|
||||||
|
scale = 1.0*memsize/available
|
||||||
|
for donors_iter in donors:
|
||||||
|
id, mem = donors_iter
|
||||||
|
memborrowed = mem*scale*REQ_SAFETY_NET_FACTOR
|
||||||
|
print 'borrow' , memborrowed, 'from', id
|
||||||
|
memtarget = int(domdict[id].memory_actual - memborrowed)
|
||||||
|
request.append((id, memtarget))
|
||||||
|
return request
|
||||||
|
# REQ_SAFETY_NET_FACTOR is a bit greater that 1. So that if the domain yields a bit less than requested, due
|
||||||
|
# to e.g. rounding errors, we will not get stuck. The surplus will return to the VM during "balance" call.
|
||||||
|
|
||||||
|
def balance(xenfree, domdict):
|
||||||
|
total_memneeded = 0
|
||||||
|
total_mem_pref = 0
|
||||||
|
recalc_mem_used(domdict)
|
||||||
|
#pass 1: compute the above "total" values
|
||||||
|
for i in domdict.keys():
|
||||||
|
if domdict[i].meminfo is None:
|
||||||
|
continue
|
||||||
|
need = memneeded(domdict[i])
|
||||||
|
print 'domain' , i, 'act/pref', domdict[i].memory_actual, prefmem(domdict[i]), 'need=', need
|
||||||
|
total_memneeded += need
|
||||||
|
total_mem_pref += prefmem(domdict[i])
|
||||||
|
|
||||||
|
totalsum = xenfree - total_memneeded
|
||||||
|
|
||||||
|
#pass 2: redistribute "totalsum" of memory between domains, proportionally to prefmem
|
||||||
|
donors = list()
|
||||||
|
acceptors = list()
|
||||||
|
for i in domdict.keys():
|
||||||
|
if domdict[i].meminfo is None:
|
||||||
|
continue
|
||||||
|
#distribute totalsum proportionally to mempref
|
||||||
|
scale = 1.0*prefmem(domdict[i])/total_mem_pref
|
||||||
|
target_nonint = prefmem(domdict[i]) + scale*totalsum
|
||||||
|
#prevent rounding errors
|
||||||
|
target = int(0.995*target_nonint)
|
||||||
|
if (target < domdict[i].memory_actual):
|
||||||
|
donors.append((i, target))
|
||||||
|
else:
|
||||||
|
acceptors.append((i, target))
|
||||||
|
print 'balance: xenfree=', xenfree, 'requests:', donors + acceptors
|
||||||
|
return donors + acceptors
|
||||||
|
|
||||||
|
|
16
dom0/qmemman/qmemman_client.py
Executable file
16
dom0/qmemman/qmemman_client.py
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
class QMemmanClient:
|
||||||
|
|
||||||
|
def request_memory(self, amount):
|
||||||
|
self.sock = socket.socket(socket.AF_UNIX)
|
||||||
|
self.sock.connect("/var/run/qubes/qmemman.sock")
|
||||||
|
self.sock.send(str(amount)+"\n")
|
||||||
|
self.received = self.sock.recv(1024).strip()
|
||||||
|
if self.received == 'OK':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.sock.close()
|
123
dom0/qmemman/qmemman_server.py
Executable file
123
dom0/qmemman/qmemman_server.py
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
import SocketServer
|
||||||
|
import thread
|
||||||
|
import time
|
||||||
|
import xen.lowlevel.xs
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from qmemman import SystemState
|
||||||
|
|
||||||
|
system_state = SystemState()
|
||||||
|
global_lock = thread.allocate_lock()
|
||||||
|
additional_balance_delay = 0
|
||||||
|
|
||||||
|
def only_in_first_list(l1, l2):
|
||||||
|
ret=[]
|
||||||
|
for i in l1:
|
||||||
|
if not i in l2:
|
||||||
|
ret.append(i)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def get_req_node(domain_id):
|
||||||
|
return '/local/domain/'+domain_id+'/memory/meminfo'
|
||||||
|
|
||||||
|
|
||||||
|
class WatchType:
|
||||||
|
def __init__(self, fn, param):
|
||||||
|
self.fn = fn
|
||||||
|
self.param = param
|
||||||
|
|
||||||
|
class XS_Watcher:
|
||||||
|
def __init__(self):
|
||||||
|
self.handle = xen.lowlevel.xs.xs()
|
||||||
|
self.handle.watch('/local/domain', WatchType(XS_Watcher.dom_list_change, None))
|
||||||
|
self.watch_token_dict = {}
|
||||||
|
|
||||||
|
def dom_list_change(self, param):
|
||||||
|
curr = self.handle.ls('', '/local/domain')
|
||||||
|
if curr == None:
|
||||||
|
return
|
||||||
|
global_lock.acquire()
|
||||||
|
for i in only_in_first_list(curr, self.watch_token_dict.keys()):
|
||||||
|
watch = WatchType(XS_Watcher.request, i)
|
||||||
|
self.watch_token_dict[i] = watch
|
||||||
|
self.handle.watch(get_req_node(i), watch)
|
||||||
|
system_state.add_domain(i)
|
||||||
|
for i in only_in_first_list(self.watch_token_dict.keys(), curr):
|
||||||
|
self.handle.unwatch(get_req_node(i), self.watch_token_dict[i])
|
||||||
|
self.watch_token_dict.pop(i)
|
||||||
|
system_state.del_domain(i)
|
||||||
|
global_lock.release()
|
||||||
|
|
||||||
|
def request(self, domain_id):
|
||||||
|
ret = self.handle.read('', get_req_node(domain_id))
|
||||||
|
if ret == None or ret == '':
|
||||||
|
return
|
||||||
|
global_lock.acquire()
|
||||||
|
system_state.refresh_meminfo(domain_id, ret)
|
||||||
|
global_lock.release()
|
||||||
|
|
||||||
|
def watch_loop(self):
|
||||||
|
# sys.stderr = file('/var/log/qubes/qfileexchgd.errors', 'a')
|
||||||
|
while True:
|
||||||
|
result = self.handle.read_watch()
|
||||||
|
token = result[1]
|
||||||
|
token.fn(self, token.param)
|
||||||
|
|
||||||
|
|
||||||
|
class QMemmanReqHandler(SocketServer.BaseRequestHandler):
|
||||||
|
"""
|
||||||
|
The RequestHandler class for our server.
|
||||||
|
|
||||||
|
It is instantiated once per connection to the server, and must
|
||||||
|
override the handle() method to implement communication to the
|
||||||
|
client.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
# self.request is the TCP socket connected to the client
|
||||||
|
while True:
|
||||||
|
self.data = self.request.recv(1024).strip()
|
||||||
|
if len(self.data) == 0:
|
||||||
|
print 'EOF'
|
||||||
|
return
|
||||||
|
if self.data == "DONE":
|
||||||
|
return
|
||||||
|
global_lock.acquire()
|
||||||
|
if system_state.do_balloon(int(self.data)):
|
||||||
|
resp = "OK\n"
|
||||||
|
additional_balance_delay = 5
|
||||||
|
else:
|
||||||
|
resp = "FAIL\n"
|
||||||
|
global_lock.release()
|
||||||
|
self.request.send(resp)
|
||||||
|
|
||||||
|
|
||||||
|
def start_server():
|
||||||
|
SOCK_PATH='/var/run/qubes/qmemman.sock'
|
||||||
|
try:
|
||||||
|
os.unlink(SOCK_PATH)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
os.umask(0)
|
||||||
|
server = SocketServer.UnixStreamServer(SOCK_PATH, QMemmanReqHandler)
|
||||||
|
os.umask(077)
|
||||||
|
server.serve_forever()
|
||||||
|
|
||||||
|
def start_balancer():
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
if additional_balance_delay == 0:
|
||||||
|
time.sleep(additional_balance_delay)
|
||||||
|
additional_balance_delay = 0
|
||||||
|
global_lock.acquire()
|
||||||
|
if additional_balance_delay == 0:
|
||||||
|
system_state.do_balance()
|
||||||
|
global_lock.release()
|
||||||
|
|
||||||
|
class QMemmanServer:
|
||||||
|
@staticmethod
|
||||||
|
def main():
|
||||||
|
thread.start_new_thread(start_server, tuple([]))
|
||||||
|
thread.start_new_thread(start_balancer, tuple([]))
|
||||||
|
XS_Watcher().watch_loop()
|
4
dom0/qmemman/server.py
Executable file
4
dom0/qmemman/server.py
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
from qubes.qmemman_server import QMemmanServer
|
||||||
|
|
||||||
|
QMemmanServer.main()
|
@ -29,6 +29,7 @@ import xml.parsers.expat
|
|||||||
import fcntl
|
import fcntl
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
from qmemman_client import QMemmanClient
|
||||||
|
|
||||||
# Do not use XenAPI or create/read any VM files
|
# Do not use XenAPI or create/read any VM files
|
||||||
# This is for testing only!
|
# This is for testing only!
|
||||||
@ -326,6 +327,18 @@ class QubesVm(object):
|
|||||||
|
|
||||||
return mem
|
return mem
|
||||||
|
|
||||||
|
def get_mem_dynamic_max(self):
|
||||||
|
if dry_run:
|
||||||
|
return 666
|
||||||
|
|
||||||
|
try:
|
||||||
|
mem = int(xend_session.session.xenapi.VM.get_memory_dynamic_max(self.session_uuid))
|
||||||
|
except XenAPI.Failure:
|
||||||
|
self.refresh_xend_session()
|
||||||
|
mem = int(xend_session.session.xenapi.VM.get_memory_dynamic_max(self.session_uuid))
|
||||||
|
|
||||||
|
return mem
|
||||||
|
|
||||||
|
|
||||||
def get_cpu_total_load(self):
|
def get_cpu_total_load(self):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
@ -474,15 +487,11 @@ class QubesVm(object):
|
|||||||
if verbose:
|
if verbose:
|
||||||
print "--> Loading the VM (type = {0})...".format(self.type)
|
print "--> Loading the VM (type = {0})...".format(self.type)
|
||||||
|
|
||||||
mem_required = self.get_mem_static_max()
|
mem_required = self.get_mem_dynamic_max()
|
||||||
dom0_mem = dom0_vm.get_mem()
|
qmemman_client = QMemmanClient()
|
||||||
dom0_mem_new = dom0_mem - mem_required + self.get_free_xen_memory()
|
if not qmemman_client.request_memory(mem_required):
|
||||||
if verbose:
|
qmemman_client.close()
|
||||||
print "--> AppVM required mem : {0}".format(mem_required)
|
raise MemoryError ("ERROR: insufficient memory to start this VM")
|
||||||
print "--> Dom0 mem after launch : {0}".format(dom0_mem_new)
|
|
||||||
|
|
||||||
if dom0_mem_new < dom0_min_memory:
|
|
||||||
raise MemoryError ("ERROR: starting this VM would cause Dom0 memory to go below {0}B".format(dom0_min_memory))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused
|
xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused
|
||||||
@ -490,6 +499,8 @@ class QubesVm(object):
|
|||||||
self.refresh_xend_session()
|
self.refresh_xend_session()
|
||||||
xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused
|
xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused
|
||||||
|
|
||||||
|
qmemman_client.close() # let qmemman_daemon resume balancing
|
||||||
|
|
||||||
xid = int (xend_session.session.xenapi.VM.get_domid (self.session_uuid))
|
xid = int (xend_session.session.xenapi.VM.get_domid (self.session_uuid))
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
|
@ -65,6 +65,7 @@ cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin
|
|||||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes
|
cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer
|
ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer
|
||||||
|
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir}
|
mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir}
|
||||||
cp qvm-copy.desktop qvm-dvm.desktop $RPM_BUILD_ROOT/%{kde_service_dir}
|
cp qvm-copy.desktop qvm-dvm.desktop $RPM_BUILD_ROOT/%{kde_service_dir}
|
||||||
mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d
|
mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d
|
||||||
@ -187,6 +188,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/lib/qubes/qvm-copy-to-vm.kde
|
/usr/lib/qubes/qvm-copy-to-vm.kde
|
||||||
%attr(4755,root,root) /usr/bin/qvm-open-in-dvm
|
%attr(4755,root,root) /usr/bin/qvm-open-in-dvm
|
||||||
/usr/lib/qubes/qvm-dvm-transfer
|
/usr/lib/qubes/qvm-dvm-transfer
|
||||||
|
/usr/lib/qubes/meminfo-writer
|
||||||
%{kde_service_dir}/qvm-copy.desktop
|
%{kde_service_dir}/qvm-copy.desktop
|
||||||
%{kde_service_dir}/qvm-dvm.desktop
|
%{kde_service_dir}/qvm-dvm.desktop
|
||||||
%attr(4755,root,root) /usr/lib/qubes/qubes_penctl
|
%attr(4755,root,root) /usr/lib/qubes/qubes_penctl
|
||||||
|
@ -44,8 +44,8 @@ Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-q
|
|||||||
The Qubes core files for installation on Dom0.
|
The Qubes core files for installation on Dom0.
|
||||||
|
|
||||||
%build
|
%build
|
||||||
python -m compileall qvm-core
|
python -m compileall qvm-core qmemman
|
||||||
python -O -m compileall qvm-core
|
python -O -m compileall qvm-core qmemman
|
||||||
make -C restore
|
make -C restore
|
||||||
|
|
||||||
%install
|
%install
|
||||||
@ -67,6 +67,8 @@ cp qvm-core/qubes.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
|||||||
cp qvm-core/qubes.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp qvm-core/qubes.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp qvm-core/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp qvm-core/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
cp qvm-core/__init__.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
cp qvm-core/__init__.py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
|
cp qmemman/qmemman*py $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
|
cp qmemman/qmemman*py[co] $RPM_BUILD_ROOT%{python_sitearch}/qubes
|
||||||
|
|
||||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
cp aux-tools/patch_appvm_initramfs.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
cp aux-tools/patch_appvm_initramfs.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
@ -77,6 +79,8 @@ cp aux-tools/convert_dirtemplate2vm.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
|||||||
cp aux-tools/create_apps_for_appvm.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
cp aux-tools/create_apps_for_appvm.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
cp aux-tools/remove_appvm_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
cp aux-tools/remove_appvm_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes
|
cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
|
cp qmemman/server.py $RPM_BUILD_ROOT/usr/lib/qubes/qmemman_daemon.py
|
||||||
|
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
|
|
||||||
cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin
|
cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin
|
||||||
cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes
|
cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
@ -195,6 +199,7 @@ fi
|
|||||||
%{python_sitearch}/qubes/__init__.py
|
%{python_sitearch}/qubes/__init__.py
|
||||||
%{python_sitearch}/qubes/__init__.pyc
|
%{python_sitearch}/qubes/__init__.pyc
|
||||||
%{python_sitearch}/qubes/__init__.pyo
|
%{python_sitearch}/qubes/__init__.pyo
|
||||||
|
%{python_sitearch}/qubes/qmemman*.py*
|
||||||
/usr/lib/qubes/patch_appvm_initramfs.sh
|
/usr/lib/qubes/patch_appvm_initramfs.sh
|
||||||
/usr/lib/qubes/unbind_pci_device.sh
|
/usr/lib/qubes/unbind_pci_device.sh
|
||||||
/usr/lib/qubes/unbind_all_network_devices
|
/usr/lib/qubes/unbind_all_network_devices
|
||||||
@ -203,6 +208,8 @@ fi
|
|||||||
/usr/lib/qubes/create_apps_for_appvm.sh
|
/usr/lib/qubes/create_apps_for_appvm.sh
|
||||||
/usr/lib/qubes/remove_appvm_appmenus.sh
|
/usr/lib/qubes/remove_appvm_appmenus.sh
|
||||||
/usr/lib/qubes/qubes_pencmd
|
/usr/lib/qubes/qubes_pencmd
|
||||||
|
/usr/lib/qubes/qmemman_daemon.py*
|
||||||
|
/usr/lib/qubes/meminfo-writer
|
||||||
%attr(770,root,qubes) %dir /var/lib/qubes
|
%attr(770,root,qubes) %dir /var/lib/qubes
|
||||||
%attr(770,root,qubes) %dir /var/lib/qubes/vm-templates
|
%attr(770,root,qubes) %dir /var/lib/qubes/vm-templates
|
||||||
%attr(770,root,qubes) %dir /var/lib/qubes/appvms
|
%attr(770,root,qubes) %dir /var/lib/qubes/appvms
|
||||||
|
Loading…
Reference in New Issue
Block a user