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

Merge branch 'master' into unstable

This commit is contained in:
Julien Duponchelle 2015-06-03 18:34:46 +02:00
commit a1204dca0a
10 changed files with 105 additions and 15 deletions

View File

@ -1,5 +1,23 @@
# Change Log # Change Log
## 1.3.4 02/06/15
* Drop useless dependencie dateutil
* Check if port or adapter is connected before starting/stopping a packet capture. Fixes #196.
* Prevent users to add links to running Qemu VMs and start a capture on running VirtualBox VMs.
* Fixes bug: couldn't set PCMCIA disk1 size for IOS routers.
* Fix crash if you pass an invalid hostname
* Catch VPCS kill errors
* Raise a VirtualBox error if adapter doesn't exists
* Ignore VirtualBox VM Name with a carriage return in name
* Cleanup the temporary project after modules have been notified of the path change
* Do not return error if we can't remove the old project directory
* Catch encoding errors in windows logger
* Use setter for the qemu_path (allow to pass only the binary name)
* Fixes TAP connection when using VPCS.
* Fix crash launching qemu on OSX from another location.
* Adds NAT NIO in device schema validation so they can return an error that it is not supported.
## 1.3.3 14/05/15 ## 1.3.3 14/05/15
* Check for empty iourc path. * Check for empty iourc path.

View File

@ -52,7 +52,7 @@ class CrashReport:
Report crash to a third party service Report crash to a third party service
""" """
DSN = "sync+https://9e6f04df72c74b6894a6dcd2928d069e:2035d1beb1654136b170f1e91f05ee51@app.getsentry.com/38482" DSN = "sync+https://1d821222775c4cf3a66ee462e22780df:4f95f621d9b54d6a8afe0d92ed076969@app.getsentry.com/38482"
if hasattr(sys, "frozen"): if hasattr(sys, "frozen"):
cacert = get_resource("cacert.pem") cacert = get_resource("cacert.pem")
if cacert is not None and os.path.isfile(cacert): if cacert is not None and os.path.isfile(cacert):

View File

@ -298,6 +298,9 @@ class EthernetSwitch(Device):
nio = self._nios[port_number] nio = self._nios[port_number]
if not nio:
raise DynamipsError("Port {} is not connected".format(port_number))
data_link_type = data_link_type.lower() data_link_type = data_link_type.lower()
if data_link_type.startswith("dlt_"): if data_link_type.startswith("dlt_"):
data_link_type = data_link_type[4:] data_link_type = data_link_type[4:]
@ -324,6 +327,10 @@ class EthernetSwitch(Device):
raise DynamipsError("Port {} is not allocated".format(port_number)) raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number] nio = self._nios[port_number]
if not nio:
raise DynamipsError("Port {} is not connected".format(port_number))
yield from nio.unbind_filter("both") yield from nio.unbind_filter("both")
log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
id=self._id, id=self._id,

View File

@ -1318,6 +1318,10 @@ class Router(BaseVM):
nio = adapter.get_nio(port_number) nio = adapter.get_nio(port_number)
if not nio:
raise DynamipsError("Port {slot_number}/{port_number} is not connected".format(slot_number=slot_number,
port_number=port_number))
if nio.input_filter[0] is not None and nio.output_filter[0] is not None: if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
raise DynamipsError("Port {port_number} has already a filter applied on {adapter}".format(adapter=adapter, raise DynamipsError("Port {port_number} has already a filter applied on {adapter}".format(adapter=adapter,
port_number=port_number)) port_number=port_number))
@ -1350,6 +1354,11 @@ class Router(BaseVM):
port_number=port_number)) port_number=port_number))
nio = adapter.get_nio(port_number) nio = adapter.get_nio(port_number)
if not nio:
raise DynamipsError("Port {slot_number}/{port_number} is not connected".format(slot_number=slot_number,
port_number=port_number))
yield from nio.unbind_filter("both") yield from nio.unbind_filter("both")
log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name, log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name,

View File

@ -46,13 +46,18 @@ class Qemu(BaseManager):
:returns: List of folders where Qemu binaries MAY reside. :returns: List of folders where Qemu binaries MAY reside.
""" """
<<<<<<< HEAD
paths = [] paths = []
=======
qemus = []
paths = set()
>>>>>>> master
try: try:
paths.append(os.getcwd()) paths.add(os.getcwd())
except FileNotFoundError: except FileNotFoundError:
log.warning("The current working directory doesn't exist") log.warning("The current working directory doesn't exist")
if "PATH" in os.environ: if "PATH" in os.environ:
paths.extend(os.environ["PATH"].split(os.pathsep)) paths.update(os.environ["PATH"].split(os.pathsep))
else: else:
log.warning("The PATH environment variable doesn't exist") log.warning("The PATH environment variable doesn't exist")
# look for Qemu binaries in the current working directory and $PATH # look for Qemu binaries in the current working directory and $PATH
@ -63,20 +68,25 @@ class Qemu(BaseManager):
exec_dir = os.path.dirname(os.path.abspath(sys.executable)) exec_dir = os.path.dirname(os.path.abspath(sys.executable))
for f in os.listdir(exec_dir): for f in os.listdir(exec_dir):
if f.lower().startswith("qemu"): if f.lower().startswith("qemu"):
paths.append(os.path.join(exec_dir, f)) paths.add(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.add(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"]):
paths.append(os.path.join(os.environ["PROGRAMFILES"], "qemu")) paths.add(os.path.join(os.environ["PROGRAMFILES"], "qemu"))
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
<<<<<<< HEAD
paths.extend(["/usr/bin", "/usr/local/bin", "/opt/local/bin"]) paths.extend(["/usr/bin", "/usr/local/bin", "/opt/local/bin"])
=======
paths.update(["/usr/local/bin", "/opt/local/bin"])
>>>>>>> master
if hasattr(sys, "frozen"): if hasattr(sys, "frozen"):
try: try:
paths.append(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/"))) paths.add(os.path.abspath(os.path.join(os.getcwd(), "../../../qemu/bin/")))
# If the user run the server by hand from outside # If the user run the server by hand from outside
except FileNotFoundError: except FileNotFoundError:
<<<<<<< HEAD
paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"]) paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"])
return paths return paths
@ -90,6 +100,10 @@ class Qemu(BaseManager):
qemus = [] qemus = []
for path in Qemu.paths_list(): for path in Qemu.paths_list():
=======
paths.add("/Applications/GNS3.app/Contents/Resources/qemu/bin")
for path in paths:
>>>>>>> master
try: try:
for f in os.listdir(path): for f in os.listdir(path):
if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \ if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \

View File

@ -168,7 +168,7 @@ class VirtualBox(BaseManager):
vms = [] vms = []
result = yield from self.execute("list", ["vms"]) result = yield from self.execute("list", ["vms"])
for line in result: for line in result:
if line[0] != '"' or line[-1:] != "}": if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
continue # Broken output (perhaps a carriage return in VM name continue # Broken output (perhaps a carriage return in VM name
vmname, _ = line.rsplit(' ', 1) vmname, _ = line.rsplit(' ', 1)
vmname = vmname.strip('"') vmname = vmname.strip('"')

View File

@ -837,12 +837,16 @@ class VirtualBoxVM(BaseVM):
vm_state = yield from self._get_vm_state() vm_state = yield from self._get_vm_state()
if vm_state == "running": if vm_state == "running":
# dynamically configure an UDP tunnel on the VirtualBox adapter if isinstance(nio, NIOUDP):
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1)) # dynamically configure an UDP tunnel on the VirtualBox adapter
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport)) yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost)) yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport)) yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1)) yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
elif isinstance(nio, NIONAT):
yield from self._control_vm("nic{} nat".format(adapter_number + 1))
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
adapter.add_nio(0, nio) adapter.add_nio(0, nio)
log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name, log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
@ -903,6 +907,10 @@ class VirtualBoxVM(BaseVM):
raise VirtualBoxError("Sorry, packet capturing on a started VirtualBox VM is not supported.") raise VirtualBoxError("Sorry, packet capturing on a started VirtualBox VM is not supported.")
nio = adapter.get_nio(0) nio = adapter.get_nio(0)
if not nio:
raise VirtualBoxError("Adapter {} is not connected".format(adapter_number))
if nio.capturing: if nio.capturing:
raise VirtualBoxError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number)) raise VirtualBoxError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number))
@ -925,6 +933,10 @@ class VirtualBoxVM(BaseVM):
adapter_number=adapter_number)) adapter_number=adapter_number))
nio = adapter.get_nio(0) nio = adapter.get_nio(0)
if not nio:
raise VirtualBoxError("Adapter {} is not connected".format(adapter_number))
nio.stopPacketCapture() nio.stopPacketCapture()
log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name, log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,

View File

@ -83,6 +83,28 @@ class Route(object):
def delete(cls, path, *args, **kw): def delete(cls, path, *args, **kw):
return cls._route('DELETE', path, *args, **kw) return cls._route('DELETE', path, *args, **kw)
@classmethod
def authenticate(cls, request, route, server_config):
"""
Ask user for authentication
:returns: Response if you need to auth the user otherwise None
"""
user = server_config.get("user", "").strip()
password = server_config.get("password", "").strip()
if len(user) == 0:
return
if "AUTHORIZATION" in request.headers:
if request.headers["AUTHORIZATION"] == aiohttp.helpers.BasicAuth(user, password).encode():
return
response = Response(request=request, route=route)
response.set_status(401)
response.headers["WWW-Authenticate"] = 'Basic realm="GNS3 server"'
return response
@classmethod @classmethod
def _route(cls, method, path, *args, **kw): def _route(cls, method, path, *args, **kw):
# This block is executed only the first time # This block is executed only the first time
@ -119,6 +141,13 @@ class Route(object):
def control_schema(request): def control_schema(request):
# This block is executed at each method call # This block is executed at each method call
server_config = Config.instance().get_section_config("Server")
# Authenticate
response = cls.authenticate(request, route, server_config)
if response:
return response
# Non API call # Non API call
if api_version is None or raw is True: if api_version is None or raw is True:
response = Response(request=request, route=route, output_schema=output_schema) response = Response(request=request, route=route, output_schema=output_schema)
@ -129,7 +158,6 @@ class Route(object):
# API call # API call
try: try:
request = yield from parse_request(request, input_schema) request = yield from parse_request(request, input_schema)
server_config = Config.instance().get_section_config("Server")
record_file = server_config.get("record") record_file = server_config.get("record")
if record_file: if record_file:
try: try:

View File

@ -2,3 +2,4 @@ jsonschema>=2.4.0
aiohttp>=0.15.1 aiohttp>=0.15.1
Jinja2>=2.7.3 Jinja2>=2.7.3
raven>=5.2.0 raven>=5.2.0
netifaces>=0.10.4

View File

@ -75,6 +75,7 @@ def test_list_images(manager, loop):
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Carriage', '"Carriage',
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', 'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'',
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', '"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}'] '"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']