mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
Merge branch 'master' into unstable
This commit is contained in:
commit
a1204dca0a
18
CHANGELOG
18
CHANGELOG
@ -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.
|
||||||
|
@ -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):
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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 \
|
||||||
|
@ -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('"')
|
||||||
|
@ -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":
|
||||||
|
if isinstance(nio, NIOUDP):
|
||||||
# dynamically configure an UDP tunnel on the VirtualBox adapter
|
# dynamically configure an UDP tunnel on the VirtualBox adapter
|
||||||
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||||
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||||
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||||
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
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,
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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}']
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user