mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-01-31 05:13:49 +02:00
Merge branch 'master' into dev
This commit is contained in:
commit
00f49e337d
@ -73,8 +73,8 @@ import logging
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
ADAPTER_MATRIX = {"C7200_IO_2FE": C7200_IO_2FE,
|
ADAPTER_MATRIX = {"C7200-IO-2FE": C7200_IO_2FE,
|
||||||
"C7200_IO_FE": C7200_IO_FE,
|
"C7200-IO-FE": C7200_IO_FE,
|
||||||
"C7200-IO-GE-E": C7200_IO_GE_E,
|
"C7200-IO-GE-E": C7200_IO_GE_E,
|
||||||
"NM-16ESW": NM_16ESW,
|
"NM-16ESW": NM_16ESW,
|
||||||
"NM-1E": NM_1E,
|
"NM-1E": NM_1E,
|
||||||
@ -468,7 +468,7 @@ class VM(object):
|
|||||||
except DynamipsError as e:
|
except DynamipsError as e:
|
||||||
self.send_custom_error(str(e))
|
self.send_custom_error(str(e))
|
||||||
return
|
return
|
||||||
elif name.startswith("slot") and value == None:
|
elif name.startswith("slot") and value is None:
|
||||||
slot_id = int(name[-1])
|
slot_id = int(name[-1])
|
||||||
if router.slots[slot_id]:
|
if router.slots[slot_id]:
|
||||||
try:
|
try:
|
||||||
|
@ -212,7 +212,7 @@ class Hypervisor(DynamipsHypervisor):
|
|||||||
cwd=self._working_dir)
|
cwd=self._working_dir)
|
||||||
log.info("Dynamips started PID={}".format(self._process.pid))
|
log.info("Dynamips started PID={}".format(self._process.pid))
|
||||||
self._started = True
|
self._started = True
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
log.error("could not start Dynamips: {}".format(e))
|
log.error("could not start Dynamips: {}".format(e))
|
||||||
raise DynamipsError("could not start Dynamips: {}".format(e))
|
raise DynamipsError("could not start Dynamips: {}".format(e))
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
|
|||||||
|
|
||||||
from ..dynamips_error import DynamipsError
|
from ..dynamips_error import DynamipsError
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from ..adapters.c7200_io_2fe import C7200_IO_2FE
|
from ..adapters.c7200_io_fe import C7200_IO_FE
|
||||||
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
|
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -70,7 +70,7 @@ class C7200(Router):
|
|||||||
if npe == "npe-g2":
|
if npe == "npe-g2":
|
||||||
self.slot_add_binding(0, C7200_IO_GE_E())
|
self.slot_add_binding(0, C7200_IO_GE_E())
|
||||||
else:
|
else:
|
||||||
self.slot_add_binding(0, C7200_IO_2FE())
|
self.slot_add_binding(0, C7200_IO_FE())
|
||||||
|
|
||||||
def defaults(self):
|
def defaults(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1324,7 +1324,7 @@ class Router(object):
|
|||||||
adapter=current_adapter))
|
adapter=current_adapter))
|
||||||
|
|
||||||
# Only c7200, c3600 and c3745 (NM-4T only) support new adapter while running
|
# Only c7200, c3600 and c3745 (NM-4T only) support new adapter while running
|
||||||
if self.is_running() and not (self._platform == 'c7200'
|
if self.is_running() and not ((self._platform == 'c7200' and not str(adapter).startswith('C7200'))
|
||||||
and not (self._platform == 'c3600' and self.chassis == '3660')
|
and not (self._platform == 'c3600' and self.chassis == '3660')
|
||||||
and not (self._platform == 'c3745' and adapter == 'NM-4T')):
|
and not (self._platform == 'c3745' and adapter == 'NM-4T')):
|
||||||
raise DynamipsError("Adapter {adapter} cannot be added while router {name} is running".format(adapter=adapter,
|
raise DynamipsError("Adapter {adapter} cannot be added while router {name} is running".format(adapter=adapter,
|
||||||
@ -1369,7 +1369,7 @@ class Router(object):
|
|||||||
slot_id=slot_id))
|
slot_id=slot_id))
|
||||||
|
|
||||||
# Only c7200, c3600 and c3745 (NM-4T only) support to remove adapter while running
|
# Only c7200, c3600 and c3745 (NM-4T only) support to remove adapter while running
|
||||||
if self.is_running() and not (self._platform == 'c7200'
|
if self.is_running() and not ((self._platform == 'c7200' and not str(adapter).startswith('C7200'))
|
||||||
and not (self._platform == 'c3600' and self.chassis == '3660')
|
and not (self._platform == 'c3600' and self.chassis == '3660')
|
||||||
and not (self._platform == 'c3745' and adapter == 'NM-4T')):
|
and not (self._platform == 'c3745' and adapter == 'NM-4T')):
|
||||||
raise DynamipsError("Adapter {adapter} cannot be removed while router {name} is running".format(adapter=adapter,
|
raise DynamipsError("Adapter {adapter} cannot be removed while router {name} is running".format(adapter=adapter,
|
||||||
|
@ -509,7 +509,7 @@ class IOUDevice(object):
|
|||||||
cwd=self._working_dir)
|
cwd=self._working_dir)
|
||||||
|
|
||||||
log.info("iouyap started PID={}".format(self._iouyap_process.pid))
|
log.info("iouyap started PID={}".format(self._iouyap_process.pid))
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
iouyap_stdout = self.read_iouyap_stdout()
|
iouyap_stdout = self.read_iouyap_stdout()
|
||||||
log.error("could not start iouyap: {}\n{}".format(e, iouyap_stdout))
|
log.error("could not start iouyap: {}\n{}".format(e, iouyap_stdout))
|
||||||
raise IOUError("Could not start iouyap: {}\n{}".format(e, iouyap_stdout))
|
raise IOUError("Could not start iouyap: {}\n{}".format(e, iouyap_stdout))
|
||||||
@ -521,7 +521,7 @@ class IOUDevice(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(["ldd", self._path])
|
output = subprocess.check_output(["ldd", self._path])
|
||||||
except (FileNotFoundError, subprocess.CalledProcessError) as e:
|
except (FileNotFoundError, subprocess.SubprocessError) as e:
|
||||||
log.warn("could not determine the shared library dependencies for {}: {}".format(self._path, e))
|
log.warn("could not determine the shared library dependencies for {}: {}".format(self._path, e))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -583,7 +583,7 @@ class IOUDevice(object):
|
|||||||
self._started = True
|
self._started = True
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
raise IOUError("could not start IOU: {}: 32-bit binary support is probably not installed".format(e))
|
raise IOUError("could not start IOU: {}: 32-bit binary support is probably not installed".format(e))
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
iou_stdout = self.read_iou_stdout()
|
iou_stdout = self.read_iou_stdout()
|
||||||
log.error("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
|
log.error("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
|
||||||
raise IOUError("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
|
raise IOUError("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
|
||||||
@ -761,7 +761,7 @@ class IOUDevice(object):
|
|||||||
command.extend(["-l"])
|
command.extend(["-l"])
|
||||||
else:
|
else:
|
||||||
raise IOUError("layer 1 keepalive messages are not supported by {}".format(os.path.basename(self._path)))
|
raise IOUError("layer 1 keepalive messages are not supported by {}".format(os.path.basename(self._path)))
|
||||||
except (OSError, subprocess.CalledProcessError) as e:
|
except subprocess.SubprocessError as e:
|
||||||
log.warn("could not determine if layer 1 keepalive messages are supported by {}: {}".format(os.path.basename(self._path), e))
|
log.warn("could not determine if layer 1 keepalive messages are supported by {}: {}".format(os.path.basename(self._path), e))
|
||||||
|
|
||||||
def _build_command(self):
|
def _build_command(self):
|
||||||
|
@ -601,14 +601,14 @@ class Qemu(IModule):
|
|||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
return ""
|
return ""
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output([qemu_path, "--version"])
|
output = subprocess.check_output([qemu_path, "-version"])
|
||||||
match = re.search("QEMU emulator version ([0-9a-z\-\.]+)", output.decode("utf-8"))
|
match = re.search("version\s+([0-9a-z\-\.]+)", output.decode("utf-8"))
|
||||||
if match:
|
if match:
|
||||||
version = match.group(1)
|
version = match.group(1)
|
||||||
return version
|
return version
|
||||||
else:
|
else:
|
||||||
raise QemuError("Could not determine the Qemu version for {}".format(qemu_path))
|
raise QemuError("Could not determine the Qemu version for {}".format(qemu_path))
|
||||||
except (OSError, subprocess.CalledProcessError) as e:
|
except subprocess.SubprocessError as e:
|
||||||
raise QemuError("Error while looking for the Qemu version: {}".format(e))
|
raise QemuError("Error while looking for the Qemu version: {}".format(e))
|
||||||
|
|
||||||
@IModule.route("qemu.qemu_list")
|
@IModule.route("qemu.qemu_list")
|
||||||
@ -625,7 +625,13 @@ class Qemu(IModule):
|
|||||||
# look for Qemu binaries in the current working directory and $PATH
|
# look for Qemu binaries in the current working directory and $PATH
|
||||||
if sys.platform.startswith("win"):
|
if sys.platform.startswith("win"):
|
||||||
# add specific Windows paths
|
# add specific Windows paths
|
||||||
paths.append(os.path.join(os.getcwd(), "qemu"))
|
if hasattr(sys, "frozen"):
|
||||||
|
# add any qemu dir in the same location as gns3server.exe to the list of paths
|
||||||
|
exec_dir = os.path.dirname(os.path.abspath(sys.executable))
|
||||||
|
for f in os.listdir(exec_dir):
|
||||||
|
if f.lower().startswith("qemu"):
|
||||||
|
paths.append(os.path.join(exec_dir, f))
|
||||||
|
|
||||||
if "PROGRAMFILES(X86)" in os.environ and os.path.exists(os.environ["PROGRAMFILES(X86)"]):
|
if "PROGRAMFILES(X86)" in os.environ and os.path.exists(os.environ["PROGRAMFILES(X86)"]):
|
||||||
paths.append(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
|
paths.append(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
|
||||||
if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]):
|
if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]):
|
||||||
@ -633,15 +639,18 @@ class Qemu(IModule):
|
|||||||
elif sys.platform.startswith("darwin"):
|
elif sys.platform.startswith("darwin"):
|
||||||
# add specific locations on Mac OS X regardless of what's in $PATH
|
# add specific locations on Mac OS X regardless of what's in $PATH
|
||||||
paths.extend(["/usr/local/bin", "/opt/local/bin"])
|
paths.extend(["/usr/local/bin", "/opt/local/bin"])
|
||||||
|
if hasattr(sys, "frozen"):
|
||||||
|
paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/")))
|
||||||
for path in paths:
|
for path in paths:
|
||||||
try:
|
try:
|
||||||
for f in os.listdir(path):
|
for f in os.listdir(path):
|
||||||
if (f.startswith("qemu-system") or f == "qemu" or f == "qemu.exe") and os.access(os.path.join(path, f), os.X_OK):
|
if (f.startswith("qemu-system") or f == "qemu" or f == "qemu.exe") and \
|
||||||
|
os.access(os.path.join(path, f), os.X_OK) and \
|
||||||
|
os.path.isfile(os.path.join(path, f)):
|
||||||
qemu_path = os.path.join(path, f)
|
qemu_path = os.path.join(path, f)
|
||||||
version = self._get_qemu_version(qemu_path)
|
version = self._get_qemu_version(qemu_path)
|
||||||
qemus.append({"path": qemu_path, "version": version})
|
qemus.append({"path": qemu_path, "version": version})
|
||||||
except (OSError, QemuError) as e:
|
except OSError:
|
||||||
log.warn("Could not find QEMU version for {}: {}".format(path, e))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
response = {"qemus": qemus}
|
response = {"qemus": qemus}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
QEMU VM instance.
|
QEMU VM instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import random
|
import random
|
||||||
@ -89,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
|
||||||
@ -107,6 +109,9 @@ class QemuVM(object):
|
|||||||
self._initrd = ""
|
self._initrd = ""
|
||||||
self._kernel_image = ""
|
self._kernel_image = ""
|
||||||
self._kernel_command_line = ""
|
self._kernel_command_line = ""
|
||||||
|
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))
|
||||||
|
|
||||||
@ -152,7 +157,11 @@ class QemuVM(object):
|
|||||||
"console": self._console,
|
"console": self._console,
|
||||||
"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,
|
||||||
|
"cpu_throttling": self._cpu_throttling,
|
||||||
|
"process_priority": self._process_priority
|
||||||
|
}
|
||||||
|
|
||||||
return qemu_defaults
|
return qemu_defaults
|
||||||
|
|
||||||
@ -437,6 +446,80 @@ class QemuVM(object):
|
|||||||
id=self._id,
|
id=self._id,
|
||||||
adapter_type=adapter_type))
|
adapter_type=adapter_type))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def legacy_networking(self):
|
||||||
|
"""
|
||||||
|
Returns either QEMU legacy networking commands are used.
|
||||||
|
|
||||||
|
:returns: boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._legacy_networking
|
||||||
|
|
||||||
|
@legacy_networking.setter
|
||||||
|
def legacy_networking(self, legacy_networking):
|
||||||
|
"""
|
||||||
|
Sets either QEMU legacy networking commands are used.
|
||||||
|
|
||||||
|
:param legacy_networking: boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
if legacy_networking:
|
||||||
|
log.info("QEMU VM {name} [id={id}] has enabled legacy networking".format(name=self._name, id=self._id))
|
||||||
|
else:
|
||||||
|
log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id))
|
||||||
|
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):
|
||||||
"""
|
"""
|
||||||
@ -552,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(3)
|
||||||
|
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 install it or 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.
|
||||||
@ -612,11 +773,15 @@ class QemuVM(object):
|
|||||||
cwd=self._working_dir)
|
cwd=self._working_dir)
|
||||||
log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid))
|
log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid))
|
||||||
self._started = True
|
self._started = True
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
stdout = self.read_stdout()
|
stdout = self.read_stdout()
|
||||||
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))
|
||||||
|
|
||||||
|
self._set_process_priority()
|
||||||
|
if self._cpu_throttling:
|
||||||
|
self._set_cpu_throttling()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
Stops this QEMU VM.
|
Stops this QEMU VM.
|
||||||
@ -635,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):
|
||||||
"""
|
"""
|
||||||
@ -782,7 +948,7 @@ class QemuVM(object):
|
|||||||
retcode = subprocess.call([qemu_img_path, "create", "-f", "qcow2", hda_disk, "128M"])
|
retcode = subprocess.call([qemu_img_path, "create", "-f", "qcow2", hda_disk, "128M"])
|
||||||
log.info("{} returned with {}".format(qemu_img_path, retcode))
|
log.info("{} returned with {}".format(qemu_img_path, retcode))
|
||||||
|
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
raise QemuError("Could not create disk image {}".format(e))
|
raise QemuError("Could not create disk image {}".format(e))
|
||||||
|
|
||||||
options.extend(["-hda", hda_disk])
|
options.extend(["-hda", hda_disk])
|
||||||
@ -794,7 +960,7 @@ class QemuVM(object):
|
|||||||
"backing_file={}".format(self._hdb_disk_image),
|
"backing_file={}".format(self._hdb_disk_image),
|
||||||
"-f", "qcow2", hdb_disk])
|
"-f", "qcow2", hdb_disk])
|
||||||
log.info("{} returned with {}".format(qemu_img_path, retcode))
|
log.info("{} returned with {}".format(qemu_img_path, retcode))
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
raise QemuError("Could not create disk image {}".format(e))
|
raise QemuError("Could not create disk image {}".format(e))
|
||||||
options.extend(["-hdb", hdb_disk])
|
options.extend(["-hdb", hdb_disk])
|
||||||
|
|
||||||
@ -819,16 +985,29 @@ class QemuVM(object):
|
|||||||
for adapter in self._ethernet_adapters:
|
for adapter in self._ethernet_adapters:
|
||||||
#TODO: let users specify a base mac address
|
#TODO: let users specify a base mac address
|
||||||
mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
|
mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
|
||||||
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
|
if self._legacy_networking:
|
||||||
|
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_id, mac, self._adapter_type)])
|
||||||
|
else:
|
||||||
|
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
|
||||||
nio = adapter.get_nio(0)
|
nio = adapter.get_nio(0)
|
||||||
if nio and isinstance(nio, NIO_UDP):
|
if nio and isinstance(nio, NIO_UDP):
|
||||||
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
|
if self._legacy_networking:
|
||||||
nio.rhost,
|
network_options.extend(["-net", "udp,vlan={},sport={},dport={},daddr={}".format(adapter_id,
|
||||||
nio.rport,
|
nio.lport,
|
||||||
self._host,
|
nio.rport,
|
||||||
nio.lport)])
|
nio.rhost)])
|
||||||
|
|
||||||
|
else:
|
||||||
|
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
|
||||||
|
nio.rhost,
|
||||||
|
nio.rport,
|
||||||
|
self._host,
|
||||||
|
nio.lport)])
|
||||||
else:
|
else:
|
||||||
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
|
if self._legacy_networking:
|
||||||
|
network_options.extend(["-net", "user,vlan={}".format(adapter_id)])
|
||||||
|
else:
|
||||||
|
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
|
||||||
adapter_id += 1
|
adapter_id += 1
|
||||||
|
|
||||||
return network_options
|
return network_options
|
||||||
|
@ -124,8 +124,27 @@ QEMU_UPDATE_SCHEMA = {
|
|||||||
"description": "Path to the image in the cloud object store",
|
"description": "Path to the image in the cloud object store",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"legacy_networking": {
|
||||||
|
"description": "Use QEMU legagy networking commands (-net syntax)",
|
||||||
|
"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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -62,8 +62,13 @@ class VirtualBox(IModule):
|
|||||||
|
|
||||||
# get the vboxmanage location
|
# get the vboxmanage location
|
||||||
self._vboxmanage_path = None
|
self._vboxmanage_path = None
|
||||||
if sys.platform.startswith("win") and "VBOX_INSTALL_PATH" in os.environ:
|
if sys.platform.startswith("win"):
|
||||||
self._vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
|
if "VBOX_INSTALL_PATH" in os.environ:
|
||||||
|
self._vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
|
||||||
|
elif "VBOX_MSI_INSTALL_PATH" in os.environ:
|
||||||
|
self._vboxmanage_path = os.path.join(os.environ["VBOX_MSI_INSTALL_PATH"], "VBoxManage.exe")
|
||||||
|
elif sys.platform.startswith("darwin"):
|
||||||
|
self._vboxmanage_path = "/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
|
||||||
else:
|
else:
|
||||||
config = Config.instance()
|
config = Config.instance()
|
||||||
vbox_config = config.get_section_config(name.upper())
|
vbox_config = config.get_section_config(name.upper())
|
||||||
@ -716,12 +721,10 @@ class VirtualBox(IModule):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True, timeout=30)
|
result = subprocess.check_output(command, stderr=subprocess.STDOUT, timeout=30)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.SubprocessError as e:
|
||||||
raise VirtualBoxError("Could not execute VBoxManage {}".format(e))
|
raise VirtualBoxError("Could not execute VBoxManage {}".format(e))
|
||||||
except subprocess.TimeoutExpired:
|
return result.decode("utf-8")
|
||||||
raise VirtualBoxError("VBoxManage has timed out")
|
|
||||||
return result
|
|
||||||
|
|
||||||
@IModule.route("virtualbox.vm_list")
|
@IModule.route("virtualbox.vm_list")
|
||||||
def vm_list(self, request):
|
def vm_list(self, request):
|
||||||
@ -753,7 +756,13 @@ class VirtualBox(IModule):
|
|||||||
for line in result.splitlines():
|
for line in result.splitlines():
|
||||||
vmname, uuid = line.rsplit(' ', 1)
|
vmname, uuid = line.rsplit(' ', 1)
|
||||||
vmname = vmname.strip('"')
|
vmname = vmname.strip('"')
|
||||||
extra_data = self._execute_vboxmanage([vboxmanage_path, "getextradata", vmname, "GNS3/Clone"]).strip()
|
if vmname == "<inaccessible>":
|
||||||
|
continue # ignore inaccessible VMs
|
||||||
|
try:
|
||||||
|
extra_data = self._execute_vboxmanage([vboxmanage_path, "getextradata", vmname, "GNS3/Clone"]).strip()
|
||||||
|
except VirtualBoxError as e:
|
||||||
|
self.send_custom_error(str(e))
|
||||||
|
return
|
||||||
if not extra_data == "Value: yes":
|
if not extra_data == "Value: yes":
|
||||||
vms.append(vmname)
|
vms.append(vmname)
|
||||||
|
|
||||||
|
@ -322,8 +322,8 @@ class VirtualBoxVM(object):
|
|||||||
self._allocated_console_ports.remove(self.console)
|
self._allocated_console_ports.remove(self.console)
|
||||||
|
|
||||||
if self._linked_clone:
|
if self._linked_clone:
|
||||||
|
hdd_table = []
|
||||||
if os.path.exists(self._working_dir):
|
if os.path.exists(self._working_dir):
|
||||||
hdd_table = []
|
|
||||||
hdd_files = self._get_all_hdd_files()
|
hdd_files = self._get_all_hdd_files()
|
||||||
vm_info = self._get_vm_info()
|
vm_info = self._get_vm_info()
|
||||||
for entry, value in vm_info.items():
|
for entry, value in vm_info.items():
|
||||||
@ -550,17 +550,17 @@ class VirtualBoxVM(object):
|
|||||||
command.extend(args)
|
command.extend(args)
|
||||||
log.debug("Execute vboxmanage command: {}".format(command))
|
log.debug("Execute vboxmanage command: {}".format(command))
|
||||||
try:
|
try:
|
||||||
result = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True, timeout=timeout)
|
result = subprocess.check_output(command, stderr=subprocess.STDOUT, timeout=timeout)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
if e.output:
|
if e.output:
|
||||||
# only the first line of the output is useful
|
# only the first line of the output is useful
|
||||||
virtualbox_error = e.output.splitlines()[0]
|
virtualbox_error = e.output.decode("utf-8").splitlines()[0]
|
||||||
raise VirtualBoxError("{}".format(virtualbox_error))
|
raise VirtualBoxError("{}".format(virtualbox_error))
|
||||||
else:
|
else:
|
||||||
raise VirtualBoxError("{}".format(e))
|
raise VirtualBoxError("{}".format(e))
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.SubprocessError as e:
|
||||||
raise VirtualBoxError("VBoxManage has timed out")
|
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e))
|
||||||
return result.splitlines()
|
return result.decode("utf-8").splitlines()
|
||||||
|
|
||||||
def _get_vm_info(self):
|
def _get_vm_info(self):
|
||||||
"""
|
"""
|
||||||
|
@ -346,7 +346,7 @@ class VPCSDevice(object):
|
|||||||
raise VPCSError("VPCS executable version must be >= 0.5b1")
|
raise VPCSError("VPCS executable version must be >= 0.5b1")
|
||||||
else:
|
else:
|
||||||
raise VPCSError("Could not determine the VPCS version for {}".format(self._path))
|
raise VPCSError("Could not determine the VPCS version for {}".format(self._path))
|
||||||
except (OSError, subprocess.CalledProcessError) as e:
|
except subprocess.SubprocessError as e:
|
||||||
raise VPCSError("Error while looking for the VPCS version: {}".format(e))
|
raise VPCSError("Error while looking for the VPCS version: {}".format(e))
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
@ -386,7 +386,7 @@ class VPCSDevice(object):
|
|||||||
creationflags=flags)
|
creationflags=flags)
|
||||||
log.info("VPCS instance {} started PID={}".format(self._id, self._process.pid))
|
log.info("VPCS instance {} started PID={}".format(self._id, self._process.pid))
|
||||||
self._started = True
|
self._started = True
|
||||||
except OSError as e:
|
except subprocess.SubprocessError as e:
|
||||||
vpcs_stdout = self.read_vpcs_stdout()
|
vpcs_stdout = self.read_vpcs_stdout()
|
||||||
log.error("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout))
|
log.error("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout))
|
||||||
raise VPCSError("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout))
|
raise VPCSError("could not start VPCS {}: {}\n{}".format(self._path, e, vpcs_stdout))
|
||||||
|
@ -184,7 +184,6 @@ class Server(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
log.info("Missing cloud.conf - disabling HTTP auth and SSL")
|
log.info("Missing cloud.conf - disabling HTTP auth and SSL")
|
||||||
|
|
||||||
|
|
||||||
router = self._create_zmq_router()
|
router = self._create_zmq_router()
|
||||||
# Add our JSON-RPC Websocket handler to Tornado
|
# Add our JSON-RPC Websocket handler to Tornado
|
||||||
self.handlers.extend([(r"/", JSONRPCWebSocket, dict(zmq_router=router))])
|
self.handlers.extend([(r"/", JSONRPCWebSocket, dict(zmq_router=router))])
|
||||||
@ -208,6 +207,7 @@ class Server(object):
|
|||||||
|
|
||||||
if parse_version(tornado.version) >= parse_version("3.1"):
|
if parse_version(tornado.version) >= parse_version("3.1"):
|
||||||
kwargs["max_buffer_size"] = 524288000 # 500 MB file upload limit
|
kwargs["max_buffer_size"] = 524288000 # 500 MB file upload limit
|
||||||
|
|
||||||
tornado_app.listen(self._port, **kwargs)
|
tornado_app.listen(self._port, **kwargs)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.EADDRINUSE: # socket already in use
|
if e.errno == errno.EADDRINUSE: # socket already in use
|
||||||
|
@ -23,5 +23,5 @@
|
|||||||
# or negative for a release candidate or beta (after the base version
|
# or negative for a release candidate or beta (after the base version
|
||||||
# number has been incremented)
|
# number has been incremented)
|
||||||
|
|
||||||
__version__ = "1.2.dev3"
|
__version__ = "1.2.2.dev1"
|
||||||
__version_info__ = (1, 2, 0, 99)
|
__version_info__ = (1, 2, 2, 0)
|
||||||
|
46
scripts/ws_client.py
Normal file
46
scripts/ws_client.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 GNS3 Technologies Inc.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from ws4py.client.threadedclient import WebSocketClient
|
||||||
|
|
||||||
|
|
||||||
|
class WSClient(WebSocketClient):
|
||||||
|
|
||||||
|
def opened(self):
|
||||||
|
|
||||||
|
print("Connection successful with {}:{}".format(self.host, self.port))
|
||||||
|
|
||||||
|
self.send('{"jsonrpc": 2.0, "method": "dynamips.settings", "params": {"path": "/usr/local/bin/dynamips", "allocate_hypervisor_per_device": true, "working_dir": "/tmp/gns3-1b4grwm3-files", "udp_end_port_range": 20000, "sparse_memory_support": true, "allocate_hypervisor_per_ios_image": true, "aux_start_port_range": 2501, "use_local_server": true, "hypervisor_end_port_range": 7700, "aux_end_port_range": 3000, "mmap_support": true, "console_start_port_range": 2001, "console_end_port_range": 2500, "hypervisor_start_port_range": 7200, "ghost_ios_support": true, "memory_usage_limit_per_hypervisor": 1024, "jit_sharing_support": false, "udp_start_port_range": 10001}}')
|
||||||
|
self.send('{"jsonrpc": 2.0, "method": "dynamips.vm.create", "id": "e8caf5be-de3d-40dd-80b9-ab6df8029570", "params": {"image": "/home/grossmj/GNS3/images/IOS/c3725-advipservicesk9-mz.124-15.T14.image", "name": "R1", "platform": "c3725", "ram": 256}}')
|
||||||
|
|
||||||
|
def closed(self, code, reason=None):
|
||||||
|
|
||||||
|
print("Closed down. Code: {} Reason: {}".format(code, reason))
|
||||||
|
|
||||||
|
def received_message(self, m):
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
if len(m) == 175:
|
||||||
|
self.close(reason='Bye bye')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
ws = WSClient('ws://localhost:8000/', protocols=['http-only', 'chat'])
|
||||||
|
ws.connect()
|
||||||
|
ws.run_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
ws.close()
|
Loading…
Reference in New Issue
Block a user