qmemman_server.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/python
  2. import SocketServer
  3. import thread
  4. import time
  5. import xen.lowlevel.xs
  6. import sys
  7. import os
  8. from qmemman import SystemState
  9. import qmemman_algo
  10. from ConfigParser import SafeConfigParser
  11. from optparse import OptionParser
  12. from qubesutils import parse_size
  13. config_path = '/etc/qubes/qmemman.conf'
  14. SOCK_PATH='/var/run/qubes/qmemman.sock'
  15. LOG_PATH='/var/log/qubes/qmemman.log'
  16. system_state = SystemState()
  17. global_lock = thread.allocate_lock()
  18. def only_in_first_list(l1, l2):
  19. ret=[]
  20. for i in l1:
  21. if not i in l2:
  22. ret.append(i)
  23. return ret
  24. def get_domain_meminfo_key(domain_id):
  25. return '/local/domain/'+domain_id+'/memory/meminfo'
  26. class WatchType:
  27. def __init__(self, fn, param):
  28. self.fn = fn
  29. self.param = param
  30. class XS_Watcher:
  31. def __init__(self):
  32. self.handle = xen.lowlevel.xs.xs()
  33. self.handle.watch('@introduceDomain', WatchType(XS_Watcher.domain_list_changed, None))
  34. self.handle.watch('@releaseDomain', WatchType(XS_Watcher.domain_list_changed, None))
  35. self.watch_token_dict = {}
  36. def domain_list_changed(self, param):
  37. curr = self.handle.ls('', '/local/domain')
  38. if curr == None:
  39. return
  40. global_lock.acquire()
  41. for i in only_in_first_list(curr, self.watch_token_dict.keys()):
  42. #new domain has been created
  43. watch = WatchType(XS_Watcher.meminfo_changed, i)
  44. self.watch_token_dict[i] = watch
  45. self.handle.watch(get_domain_meminfo_key(i), watch)
  46. system_state.add_domain(i)
  47. for i in only_in_first_list(self.watch_token_dict.keys(), curr):
  48. #domain destroyed
  49. self.handle.unwatch(get_domain_meminfo_key(i), self.watch_token_dict[i])
  50. self.watch_token_dict.pop(i)
  51. system_state.del_domain(i)
  52. global_lock.release()
  53. system_state.do_balance()
  54. def meminfo_changed(self, domain_id):
  55. untrusted_meminfo_key = self.handle.read('', get_domain_meminfo_key(domain_id))
  56. if untrusted_meminfo_key == None or untrusted_meminfo_key == '':
  57. return
  58. global_lock.acquire()
  59. system_state.refresh_meminfo(domain_id, untrusted_meminfo_key)
  60. global_lock.release()
  61. def watch_loop(self):
  62. # sys.stderr = file('/var/log/qubes/qfileexchgd.errors', 'a')
  63. while True:
  64. result = self.handle.read_watch()
  65. token = result[1]
  66. token.fn(self, token.param)
  67. class QMemmanReqHandler(SocketServer.BaseRequestHandler):
  68. """
  69. The RequestHandler class for our server.
  70. It is instantiated once per connection to the server, and must
  71. override the handle() method to implement communication to the
  72. client.
  73. """
  74. def handle(self):
  75. got_lock = False
  76. # self.request is the TCP socket connected to the client
  77. while True:
  78. self.data = self.request.recv(1024).strip()
  79. if len(self.data) == 0:
  80. print 'EOF'
  81. if got_lock:
  82. global_lock.release()
  83. return
  84. if got_lock:
  85. print 'Second request over qmemman.sock ?'
  86. return
  87. global_lock.acquire()
  88. got_lock = True
  89. if system_state.do_balloon(int(self.data)):
  90. resp = "OK\n"
  91. else:
  92. resp = "FAIL\n"
  93. self.request.send(resp)
  94. def start_server(server):
  95. server.serve_forever()
  96. class QMemmanServer:
  97. @staticmethod
  98. def main():
  99. usage = "usage: %prog [options]"
  100. parser = OptionParser(usage)
  101. parser.add_option("-c", "--config", action="store", dest="config", default=config_path)
  102. (options, args) = parser.parse_args()
  103. logfd = os.open(LOG_PATH, os.O_WRONLY|os.O_APPEND|os.O_CREAT, 0644)
  104. if logfd < 0:
  105. print sys.stderr, "ERROR: Failed to open log file (%s)" % LOG_PATH
  106. exit(1)
  107. # reinitialize python stdout/err
  108. sys.stdout.flush()
  109. sys.stderr.flush()
  110. os.dup2(logfd, 1)
  111. os.dup2(logfd, 2)
  112. os.close(logfd)
  113. devnull = os.open('/dev/null', os.O_RDONLY)
  114. os.dup2(devnull, 0)
  115. config = SafeConfigParser({
  116. 'vm-min-mem': str(qmemman_algo.MIN_PREFMEM),
  117. 'dom0-mem-boost': str(qmemman_algo.DOM0_MEM_BOOST),
  118. 'cache-margin-factor': str(qmemman_algo.CACHE_FACTOR)
  119. })
  120. config.read(options.config)
  121. if config.has_section('global'):
  122. qmemman_algo.MIN_PREFMEM = parse_size(config.get('global', 'vm-min-mem'))
  123. qmemman_algo.DOM0_MEM_BOOST = parse_size(config.get('global', 'dom0-mem-boost'))
  124. qmemman_algo.CACHE_FACTOR = config.getfloat('global', 'cache-margin-factor')
  125. print "values: %s, %s, %s" % (str(qmemman_algo.MIN_PREFMEM), str(qmemman_algo.DOM0_MEM_BOOST), str(qmemman_algo.CACHE_FACTOR))
  126. try:
  127. os.unlink(SOCK_PATH)
  128. except:
  129. pass
  130. os.umask(0)
  131. server = SocketServer.UnixStreamServer(SOCK_PATH, QMemmanReqHandler)
  132. os.umask(077)
  133. if os.fork() == 0:
  134. thread.start_new_thread(start_server, tuple([server]))
  135. XS_Watcher().watch_loop()