Merge branch 'master' into dev

pull/54/head
Jerry Seutter 10 years ago
commit 00f49e337d

@ -73,8 +73,8 @@ import logging
log = logging.getLogger(__name__)
ADAPTER_MATRIX = {"C7200_IO_2FE": C7200_IO_2FE,
"C7200_IO_FE": C7200_IO_FE,
ADAPTER_MATRIX = {"C7200-IO-2FE": C7200_IO_2FE,
"C7200-IO-FE": C7200_IO_FE,
"C7200-IO-GE-E": C7200_IO_GE_E,
"NM-16ESW": NM_16ESW,
"NM-1E": NM_1E,
@ -468,7 +468,7 @@ class VM(object):
except DynamipsError as e:
self.send_custom_error(str(e))
return
elif name.startswith("slot") and value == None:
elif name.startswith("slot") and value is None:
slot_id = int(name[-1])
if router.slots[slot_id]:
try:

@ -212,7 +212,7 @@ class Hypervisor(DynamipsHypervisor):
cwd=self._working_dir)
log.info("Dynamips started PID={}".format(self._process.pid))
self._started = True
except OSError as e:
except subprocess.SubprocessError as e:
log.error("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 .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
import logging
@ -70,7 +70,7 @@ class C7200(Router):
if npe == "npe-g2":
self.slot_add_binding(0, C7200_IO_GE_E())
else:
self.slot_add_binding(0, C7200_IO_2FE())
self.slot_add_binding(0, C7200_IO_FE())
def defaults(self):
"""

@ -1324,7 +1324,7 @@ class Router(object):
adapter=current_adapter))
# 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 == 'c3745' and adapter == 'NM-4T')):
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))
# 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 == 'c3745' and adapter == 'NM-4T')):
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)
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()
log.error("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:
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))
return
@ -583,7 +583,7 @@ class IOUDevice(object):
self._started = True
except FileNotFoundError as 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()
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))
@ -761,7 +761,7 @@ class IOUDevice(object):
command.extend(["-l"])
else:
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))
def _build_command(self):

@ -601,14 +601,14 @@ class Qemu(IModule):
if sys.platform.startswith("win"):
return ""
try:
output = subprocess.check_output([qemu_path, "--version"])
match = re.search("QEMU emulator version ([0-9a-z\-\.]+)", output.decode("utf-8"))
output = subprocess.check_output([qemu_path, "-version"])
match = re.search("version\s+([0-9a-z\-\.]+)", output.decode("utf-8"))
if match:
version = match.group(1)
return version
else:
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))
@IModule.route("qemu.qemu_list")
@ -625,7 +625,13 @@ class Qemu(IModule):
# look for Qemu binaries in the current working directory and $PATH
if sys.platform.startswith("win"):
# 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)"]):
paths.append(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu"))
if "PROGRAMFILES" in os.environ and os.path.exists(os.environ["PROGRAMFILES"]):
@ -633,15 +639,18 @@ class Qemu(IModule):
elif sys.platform.startswith("darwin"):
# add specific locations on Mac OS X regardless of what's in $PATH
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:
try:
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)
version = self._get_qemu_version(qemu_path)
qemus.append({"path": qemu_path, "version": version})
except (OSError, QemuError) as e:
log.warn("Could not find QEMU version for {}: {}".format(path, e))
except OSError:
continue
response = {"qemus": qemus}

@ -19,6 +19,7 @@
QEMU VM instance.
"""
import sys
import os
import shutil
import random
@ -89,6 +90,7 @@ class QemuVM(object):
self._command = []
self._started = False
self._process = None
self._cpulimit_process = None
self._stdout_file = ""
self._console_host = console_host
self._console_start_port_range = console_start_port_range
@ -107,6 +109,9 @@ class QemuVM(object):
self._initrd = ""
self._kernel_image = ""
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))
@ -152,7 +157,11 @@ class QemuVM(object):
"console": self._console,
"initrd": self._initrd,
"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
@ -437,6 +446,80 @@ class QemuVM(object):
id=self._id,
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
def ram(self):
"""
@ -552,6 +635,84 @@ class QemuVM(object):
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):
"""
Starts this QEMU VM.
@ -612,11 +773,15 @@ class QemuVM(object):
cwd=self._working_dir)
log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid))
self._started = True
except OSError as e:
except subprocess.SubprocessError as e:
stdout = self.read_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))
self._set_process_priority()
if self._cpu_throttling:
self._set_cpu_throttling()
def stop(self):
"""
Stops this QEMU VM.
@ -635,6 +800,7 @@ class QemuVM(object):
self._process.pid))
self._process = None
self._started = False
self._stop_cpulimit()
def suspend(self):
"""
@ -782,7 +948,7 @@ class QemuVM(object):
retcode = subprocess.call([qemu_img_path, "create", "-f", "qcow2", hda_disk, "128M"])
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))
options.extend(["-hda", hda_disk])
@ -794,7 +960,7 @@ class QemuVM(object):
"backing_file={}".format(self._hdb_disk_image),
"-f", "qcow2", hdb_disk])
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))
options.extend(["-hdb", hdb_disk])
@ -819,16 +985,29 @@ class QemuVM(object):
for adapter in self._ethernet_adapters:
#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)
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)
if nio and isinstance(nio, NIO_UDP):
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
if self._legacy_networking:
network_options.extend(["-net", "udp,vlan={},sport={},dport={},daddr={}".format(adapter_id,
nio.lport,
nio.rport,
nio.rhost)])
else:
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
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
return network_options

@ -124,8 +124,27 @@ QEMU_UPDATE_SCHEMA = {
"description": "Path to the image in the cloud object store",
"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": {
"description": "additional QEMU options",
"description": "Additional QEMU options",
"type": "string",
},
},

@ -62,8 +62,13 @@ class VirtualBox(IModule):
# get the vboxmanage location
self._vboxmanage_path = None
if sys.platform.startswith("win") and "VBOX_INSTALL_PATH" in os.environ:
self._vboxmanage_path = os.path.join(os.environ["VBOX_INSTALL_PATH"], "VBoxManage.exe")
if sys.platform.startswith("win"):
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:
config = Config.instance()
vbox_config = config.get_section_config(name.upper())
@ -716,12 +721,10 @@ class VirtualBox(IModule):
"""
try:
result = subprocess.check_output(command, stderr=subprocess.STDOUT, universal_newlines=True, timeout=30)
except subprocess.CalledProcessError as e:
result = subprocess.check_output(command, stderr=subprocess.STDOUT, timeout=30)
except subprocess.SubprocessError as e:
raise VirtualBoxError("Could not execute VBoxManage {}".format(e))
except subprocess.TimeoutExpired:
raise VirtualBoxError("VBoxManage has timed out")
return result
return result.decode("utf-8")
@IModule.route("virtualbox.vm_list")
def vm_list(self, request):
@ -753,7 +756,13 @@ class VirtualBox(IModule):
for line in result.splitlines():
vmname, uuid = line.rsplit(' ', 1)
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":
vms.append(vmname)

@ -322,8 +322,8 @@ class VirtualBoxVM(object):
self._allocated_console_ports.remove(self.console)
if self._linked_clone:
hdd_table = []
if os.path.exists(self._working_dir):
hdd_table = []
hdd_files = self._get_all_hdd_files()
vm_info = self._get_vm_info()
for entry, value in vm_info.items():
@ -550,17 +550,17 @@ class VirtualBoxVM(object):
command.extend(args)
log.debug("Execute vboxmanage command: {}".format(command))
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:
if e.output:
# 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))
else:
raise VirtualBoxError("{}".format(e))
except subprocess.TimeoutExpired:
raise VirtualBoxError("VBoxManage has timed out")
return result.splitlines()
except subprocess.SubprocessError as e:
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e))
return result.decode("utf-8").splitlines()
def _get_vm_info(self):
"""

@ -346,7 +346,7 @@ class VPCSDevice(object):
raise VPCSError("VPCS executable version must be >= 0.5b1")
else:
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))
def start(self):
@ -386,7 +386,7 @@ class VPCSDevice(object):
creationflags=flags)
log.info("VPCS instance {} started PID={}".format(self._id, self._process.pid))
self._started = True
except OSError as e:
except subprocess.SubprocessError as e:
vpcs_stdout = self.read_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))

@ -184,7 +184,6 @@ class Server(object):
except KeyError:
log.info("Missing cloud.conf - disabling HTTP auth and SSL")
router = self._create_zmq_router()
# Add our JSON-RPC Websocket handler to Tornado
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"):
kwargs["max_buffer_size"] = 524288000 # 500 MB file upload limit
tornado_app.listen(self._port, **kwargs)
except OSError as e:
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
# number has been incremented)
__version__ = "1.2.dev3"
__version_info__ = (1, 2, 0, 99)
__version__ = "1.2.2.dev1"
__version_info__ = (1, 2, 2, 0)

@ -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…
Cancel
Save