1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 17:28:08 +00:00

Support for CPU throttling and process priority for Qemu.

This commit is contained in:
Jeremy 2014-12-02 18:12:37 -07:00
parent 1e01c85be9
commit 8f53d51c05
2 changed files with 155 additions and 19 deletions

View File

@ -90,6 +90,7 @@ class QemuVM(object):
self._command = [] self._command = []
self._started = False self._started = False
self._process = None self._process = None
self._cpulimit_process = None
self._stdout_file = "" self._stdout_file = ""
self._console_host = console_host self._console_host = console_host
self._console_start_port_range = console_start_port_range self._console_start_port_range = console_start_port_range
@ -109,6 +110,8 @@ class QemuVM(object):
self._kernel_image = "" self._kernel_image = ""
self._kernel_command_line = "" self._kernel_command_line = ""
self._legacy_networking = False self._legacy_networking = False
self._cpu_throttling = 0 # means no CPU throttling
self._process_priority = "low"
working_dir_path = os.path.join(working_dir, "qemu", "vm-{}".format(self._id)) working_dir_path = os.path.join(working_dir, "qemu", "vm-{}".format(self._id))
@ -155,7 +158,10 @@ class QemuVM(object):
"initrd": self._initrd, "initrd": self._initrd,
"kernel_image": self._kernel_image, "kernel_image": self._kernel_image,
"kernel_command_line": self._kernel_command_line, "kernel_command_line": self._kernel_command_line,
"legacy_networking": self._legacy_networking} "legacy_networking": self._legacy_networking,
"cpu_throttling": self._cpu_throttling,
"process_priority": self._process_priority
}
return qemu_defaults return qemu_defaults
@ -464,6 +470,56 @@ class QemuVM(object):
log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id)) log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id))
self._legacy_networking = legacy_networking self._legacy_networking = legacy_networking
@property
def cpu_throttling(self):
"""
Returns the percentage of CPU allowed.
:returns: integer
"""
return self._cpu_throttling
@cpu_throttling.setter
def cpu_throttling(self, cpu_throttling):
"""
Sets the percentage of CPU allowed.
:param cpu_throttling: integer
"""
log.info("QEMU VM {name} [id={id}] has set the percentage of CPU allowed to {cpu}".format(name=self._name,
id=self._id,
cpu=cpu_throttling))
self._cpu_throttling = cpu_throttling
self._stop_cpulimit()
if cpu_throttling:
self._set_cpu_throttling()
@property
def process_priority(self):
"""
Returns the process priority.
:returns: string
"""
return self._process_priority
@process_priority.setter
def process_priority(self, process_priority):
"""
Sets the process priority.
:param process_priority: string
"""
log.info("QEMU VM {name} [id={id}] has set the process priority to {priority}".format(name=self._name,
id=self._id,
priority=process_priority))
self._process_priority = process_priority
@property @property
def ram(self): def ram(self):
""" """
@ -579,6 +635,84 @@ class QemuVM(object):
kernel_command_line=kernel_command_line)) kernel_command_line=kernel_command_line))
self._kernel_command_line = kernel_command_line self._kernel_command_line = kernel_command_line
def _set_process_priority(self):
"""
Changes the process priority
"""
if sys.platform.startswith("win"):
try:
import win32api
import win32con
import win32process
except ImportError:
log.error("pywin32 must be installed to change the priority class for QEMU VM {}".format(self._name))
else:
log.info("setting QEMU VM {} priority class to BELOW_NORMAL".format(self._name))
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, self._process.pid)
if self._process_priority == "realtime":
priority = win32process.REALTIME_PRIORITY_CLASS
elif self._process_priority == "very high":
priority = win32process.HIGH_PRIORITY_CLASS
elif self._process_priority == "high":
priority = win32process.ABOVE_NORMAL_PRIORITY_CLASS
elif self._process_priority == "low":
priority = win32process.BELOW_NORMAL_PRIORITY_CLASS
elif self._process_priority == "very low":
priority = win32process.IDLE_PRIORITY_CLASS
else:
priority = win32process.NORMAL_PRIORITY_CLASS
win32process.SetPriorityClass(handle, priority)
else:
if self._process_priority == "realtime":
priority = -20
elif self._process_priority == "very high":
priority = -15
elif self._process_priority == "high":
priority = -5
elif self._process_priority == "low":
priority = 5
elif self._process_priority == "very low":
priority = 19
else:
priority = 0
try:
subprocess.call(['renice', '-n', str(priority), '-p', str(self._process.pid)])
except subprocess.SubprocessError as e:
log.error("could not change process priority for QEMU VM {}: {}".format(self._name, e))
def _stop_cpulimit(self):
"""
Stops the cpulimit process.
"""
if self._cpulimit_process and self._cpulimit_process.poll() is None:
self._cpulimit_process.kill()
try:
self._process.wait(1)
except subprocess.TimeoutExpired:
log.error("could not kill cpulimit process {}".format(self._cpulimit_process.pid))
def _set_cpu_throttling(self):
"""
Limits the CPU usage for current QEMU process.
"""
if not self.is_running():
return
try:
if sys.platform.startswith("win") and hasattr(sys, "frozen"):
cpulimit_exec = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "cpulimit", "cpulimit.exe")
else:
cpulimit_exec = "cpulimit"
subprocess.Popen([cpulimit_exec, "--lazy", "--pid={}".format(self._process.pid), "--limit={}".format(self._cpu_throttling)], cwd=self._working_dir)
log.info("CPU throttled to {}%".format(self._cpu_throttling))
except FileNotFoundError:
raise QemuError("cpulimit could not be found, please deactivate CPU throttling")
except subprocess.SubprocessError as e:
raise QemuError("Could not throttle CPU: {}".format(e))
def start(self): def start(self):
""" """
Starts this QEMU VM. Starts this QEMU VM.
@ -644,23 +778,9 @@ class QemuVM(object):
log.error("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout)) log.error("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout))
raise QemuError("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout)) raise QemuError("could not start QEMU {}: {}\n{}".format(self._qemu_path, e, stdout))
# change the process priority self._set_process_priority()
if sys.platform.startswith("win"): if self._cpu_throttling:
try: self._set_cpu_throttling()
import win32api
import win32con
import win32process
except ImportError:
log.error("pywin32 must be installed to change the priority class for QEMU VM {}".format(self._name))
else:
log.info("setting QEMU VM {} priority class to BELOW_NORMAL".format(self._name))
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, self._process.pid)
win32process.SetPriorityClass(handle, win32process.BELOW_NORMAL_PRIORITY_CLASS)
else:
try:
subprocess.call(['renice', '-n', '19', '-p', str(self._process.pid)])
except subprocess.SubprocessError as e:
log.error("could not change process priority for QEMU VM {}: {}".format(self._name, e))
def stop(self): def stop(self):
""" """
@ -680,6 +800,7 @@ class QemuVM(object):
self._process.pid)) self._process.pid))
self._process = None self._process = None
self._started = False self._started = False
self._stop_cpulimit()
def suspend(self): def suspend(self):
""" """

View File

@ -128,8 +128,23 @@ QEMU_UPDATE_SCHEMA = {
"description": "Use QEMU legagy networking commands (-net syntax)", "description": "Use QEMU legagy networking commands (-net syntax)",
"type": "boolean", "type": "boolean",
}, },
"cpu_throttling": {
"description": "Percentage of CPU allowed for QEMU",
"minimum": 0,
"maximum": 800,
"type": "integer",
},
"process_priority": {
"description": "Process priority for QEMU",
"enum": ["realtime",
"very high",
"high",
"normal",
"low",
"very low"]
},
"options": { "options": {
"description": "additional QEMU options", "description": "Additional QEMU options",
"type": "string", "type": "string",
}, },
}, },