From da72a9501a7074130d5d7bf2e1da763f01fd308a Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 1 Jun 2015 11:40:42 +0200 Subject: [PATCH 01/14] Start virtualbox VM one by one Related to #190 --- gns3server/modules/virtualbox/__init__.py | 3 ++ .../modules/virtualbox/virtualbox_vm.py | 35 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/gns3server/modules/virtualbox/__init__.py b/gns3server/modules/virtualbox/__init__.py index 509b7182..23443274 100644 --- a/gns3server/modules/virtualbox/__init__.py +++ b/gns3server/modules/virtualbox/__init__.py @@ -41,6 +41,9 @@ class VirtualBox(BaseManager): super().__init__() self._vboxmanage_path = None + # It seem starting two VM in paralell can be an issue: + # https://github.com/GNS3/gns3-server/issues/190 + self.start_lock = asyncio.Lock() self._execute_lock = asyncio.Lock() @property diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index ddeb6089..21c7d655 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -178,23 +178,24 @@ class VirtualBoxVM(BaseVM): if vm_state != "poweroff" and vm_state != "saved": raise VirtualBoxError("VirtualBox VM not powered off or saved") - yield from self._set_network_options() - yield from self._set_serial_console() - - args = [self._vmname] - if self._headless: - args.extend(["--type", "headless"]) - result = yield from self.manager.execute("startvm", args) - log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id)) - log.debug("Start result: {}".format(result)) - - # add a guest property to let the VM know about the GNS3 name - yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name]) - # add a guest property to let the VM know about the GNS3 project directory - yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir]) - - if self._enable_remote_console and self._console is not None: - self._start_remote_console() + with (yield from self.manager.start_lock): + yield from self._set_network_options() + yield from self._set_serial_console() + + args = [self._vmname] + if self._headless: + args.extend(["--type", "headless"]) + result = yield from self.manager.execute("startvm", args) + log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id)) + log.debug("Start result: {}".format(result)) + + # add a guest property to let the VM know about the GNS3 name + yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name]) + # add a guest property to let the VM know about the GNS3 project directory + yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir]) + + if self._enable_remote_console and self._console is not None: + self._start_remote_console() @asyncio.coroutine def stop(self): From 05aafb9538a5b2338a8c9b0236a7edb9fd1cd13c Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Mon, 1 Jun 2015 16:16:34 +0200 Subject: [PATCH 02/14] Revert "Start virtualbox VM one by one" because it doesn't fix the issue This reverts commit da72a9501a7074130d5d7bf2e1da763f01fd308a. --- gns3server/modules/virtualbox/__init__.py | 3 -- .../modules/virtualbox/virtualbox_vm.py | 35 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/gns3server/modules/virtualbox/__init__.py b/gns3server/modules/virtualbox/__init__.py index 23443274..509b7182 100644 --- a/gns3server/modules/virtualbox/__init__.py +++ b/gns3server/modules/virtualbox/__init__.py @@ -41,9 +41,6 @@ class VirtualBox(BaseManager): super().__init__() self._vboxmanage_path = None - # It seem starting two VM in paralell can be an issue: - # https://github.com/GNS3/gns3-server/issues/190 - self.start_lock = asyncio.Lock() self._execute_lock = asyncio.Lock() @property diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 21c7d655..ddeb6089 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -178,24 +178,23 @@ class VirtualBoxVM(BaseVM): if vm_state != "poweroff" and vm_state != "saved": raise VirtualBoxError("VirtualBox VM not powered off or saved") - with (yield from self.manager.start_lock): - yield from self._set_network_options() - yield from self._set_serial_console() - - args = [self._vmname] - if self._headless: - args.extend(["--type", "headless"]) - result = yield from self.manager.execute("startvm", args) - log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id)) - log.debug("Start result: {}".format(result)) - - # add a guest property to let the VM know about the GNS3 name - yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name]) - # add a guest property to let the VM know about the GNS3 project directory - yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir]) - - if self._enable_remote_console and self._console is not None: - self._start_remote_console() + yield from self._set_network_options() + yield from self._set_serial_console() + + args = [self._vmname] + if self._headless: + args.extend(["--type", "headless"]) + result = yield from self.manager.execute("startvm", args) + log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id)) + log.debug("Start result: {}".format(result)) + + # add a guest property to let the VM know about the GNS3 name + yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name]) + # add a guest property to let the VM know about the GNS3 project directory + yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir]) + + if self._enable_remote_console and self._console is not None: + self._start_remote_console() @asyncio.coroutine def stop(self): From 9df290f1928dfd755f357c7c7018905c355bf9f5 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 1 Jun 2015 15:42:17 -0600 Subject: [PATCH 03/14] Check if port or adapter is connected before starting/stopping a packet capture. Fixes #196. --- gns3server/modules/dynamips/nodes/ethernet_switch.py | 7 +++++++ gns3server/modules/dynamips/nodes/router.py | 9 +++++++++ gns3server/modules/virtualbox/virtualbox_vm.py | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/gns3server/modules/dynamips/nodes/ethernet_switch.py b/gns3server/modules/dynamips/nodes/ethernet_switch.py index fc74e09c..2f8f0d87 100644 --- a/gns3server/modules/dynamips/nodes/ethernet_switch.py +++ b/gns3server/modules/dynamips/nodes/ethernet_switch.py @@ -298,6 +298,9 @@ class EthernetSwitch(Device): nio = self._nios[port_number] + if not nio: + raise DynamipsError("Port {} is not connected".format(port_number)) + data_link_type = data_link_type.lower() if data_link_type.startswith("dlt_"): data_link_type = data_link_type[4:] @@ -324,6 +327,10 @@ class EthernetSwitch(Device): raise DynamipsError("Port {} is not allocated".format(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") log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name, id=self._id, diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 4efee4d0..0b0d6492 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -1303,6 +1303,10 @@ class Router(BaseVM): 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: raise DynamipsError("Port {port_number} has already a filter applied on {adapter}".format(adapter=adapter, port_number=port_number)) @@ -1335,6 +1339,11 @@ class Router(BaseVM): port_number=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") log.info('Router "{name}" [{id}]: stopping packet capture on port {slot_number}/{port_number}'.format(name=self._name, diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index ddeb6089..546c2239 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -871,6 +871,10 @@ class VirtualBoxVM(BaseVM): raise VirtualBoxError("Sorry, packet capturing on a started VirtualBox VM is not supported.") nio = adapter.get_nio(0) + + if not nio: + raise VirtualBoxError("Adapter {} is not connected".format(adapter_number)) + if nio.capturing: raise VirtualBoxError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number)) @@ -893,6 +897,10 @@ class VirtualBoxVM(BaseVM): adapter_number=adapter_number)) nio = adapter.get_nio(0) + + if not nio: + raise VirtualBoxError("Adapter {} is not connected".format(adapter_number)) + nio.stopPacketCapture() log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name, From ad27fdf8b9d6487a2627448d498387109bce80d0 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 1 Jun 2015 16:29:49 -0600 Subject: [PATCH 04/14] Bump version to 1.3.4.dev2 --- gns3server/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/version.py b/gns3server/version.py index b6e4f133..6b5bdf2e 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.3.4.dev1" +__version__ = "1.3.4.dev2" __version_info__ = (1, 3, 4, -99) From d99047ce728168a86f541e93cd08ac0970ac6e66 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 14:39:21 +0200 Subject: [PATCH 05/14] Drop useless dependencie Fix #203 --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c12c2071..8dc813ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ netifaces==0.10.4 jsonschema==2.4.0 -python-dateutil==2.3 aiohttp==0.14.4 Jinja2==2.7.3 raven==5.2.0 From 887f9b298e8b05841f934711fdb562500b8ada25 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 14:44:49 +0200 Subject: [PATCH 06/14] 1.3.4 Changelog --- CHANGELOG | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index abf6dca2..cea6d503 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,23 @@ # 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 * Check for empty iourc path. From a2e51ac09010c029ba9077ce1f77e5a12ebbc9fd Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 15:35:14 +0200 Subject: [PATCH 07/14] Avoid duplicate paths in qemu binary list Fix #204 --- gns3server/modules/qemu/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index 70c1bf55..960a0beb 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -47,13 +47,13 @@ class Qemu(BaseManager): """ qemus = [] - paths = [] + paths = set() try: - paths.append(os.getcwd()) + paths.add(os.getcwd()) except FileNotFoundError: log.warning("The current working directory doesn't exist") if "PATH" in os.environ: - paths.extend(os.environ["PATH"].split(os.pathsep)) + paths.update(os.environ["PATH"].split(os.pathsep)) else: log.warning("The PATH environment variable doesn't exist") # look for Qemu binaries in the current working directory and $PATH @@ -64,21 +64,21 @@ class Qemu(BaseManager): 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)) + paths.add(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")) + paths.add(os.path.join(os.environ["PROGRAMFILES(X86)"], "qemu")) 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"): # add specific locations on Mac OS X regardless of what's in $PATH - paths.extend(["/usr/local/bin", "/opt/local/bin"]) + paths.update(["/usr/local/bin", "/opt/local/bin"]) if hasattr(sys, "frozen"): 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 except FileNotFoundError: - paths.append(["/Applications/GNS3.app/Contents/Resources/qemu/bin"]) + paths.add("/Applications/GNS3.app/Contents/Resources/qemu/bin") for path in paths: try: for f in os.listdir(path): From 57f9d875ca5c6473c508fd194e07e26166cf4745 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 16:27:48 +0200 Subject: [PATCH 08/14] Fix AttributeError: 'NIONAT' object has no attribute 'lport' for VirtualBox Fix #205 --- gns3server/modules/virtualbox/virtualbox_vm.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 546c2239..3c8d471a 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -805,12 +805,16 @@ class VirtualBoxVM(BaseVM): vm_state = yield from self._get_vm_state() if vm_state == "running": - # 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("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{} dport={}".format(adapter_number + 1, nio.rport)) - yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1)) + if isinstance(nio, NIOUDP): + # 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("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{} 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._modify_vm("--nic{} nat".format(adapter_number + 1)) + yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1)) adapter.add_nio(0, nio) log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name, From a0fe9bb4984c0d76caf6ac98da9a99fbf9d86365 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 2 Jun 2015 09:00:37 -0600 Subject: [PATCH 09/14] Control vm command has to be used instead of modify vm. Fixes #205. --- gns3server/modules/virtualbox/virtualbox_vm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index 3c8d471a..648eafd6 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -813,8 +813,8 @@ class VirtualBoxVM(BaseVM): 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._modify_vm("--nic{} nat".format(adapter_number + 1)) - yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1)) + 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) log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name, From 42a8c7147a1ed980eeffb751b5a5a5a8b6d8aa0c Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 19:48:04 +0200 Subject: [PATCH 10/14] 1.3.4 release --- gns3server/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/version.py b/gns3server/version.py index 6b5bdf2e..fa1b8089 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.3.4.dev2" -__version_info__ = (1, 3, 4, -99) +__version__ = "1.3.4" +__version_info__ = (1, 3, 4, 0) From 668cc3f0a5721bb0026815a83e1ace7747148f81 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 20:05:31 +0200 Subject: [PATCH 11/14] 1.3.5dev1 --- gns3server/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gns3server/version.py b/gns3server/version.py index fa1b8089..26313f06 100644 --- a/gns3server/version.py +++ b/gns3server/version.py @@ -23,5 +23,5 @@ # or negative for a release candidate or beta (after the base version # number has been incremented) -__version__ = "1.3.4" -__version_info__ = (1, 3, 4, 0) +__version__ = "1.3.5dev1" +__version_info__ = (1, 3, 5, -99) From a1bc815f63861d01f34acd9cb1dbacf03e73168f Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 2 Jun 2015 20:27:33 +0200 Subject: [PATCH 12/14] Update crash report key --- gns3server/crash_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/crash_report.py b/gns3server/crash_report.py index 984fca18..6b6752d5 100644 --- a/gns3server/crash_report.py +++ b/gns3server/crash_report.py @@ -51,7 +51,7 @@ class CrashReport: 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"): cacert = os.path.join(os.getcwd(), "cacert.pem") if os.path.isfile(cacert): From b344def8871b4cbce7539886040087a97be94aa0 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 3 Jun 2015 11:59:53 +0200 Subject: [PATCH 13/14] Fix crash when virtualbox list of VMS return an empty line Fix #206 --- gns3server/modules/virtualbox/__init__.py | 2 +- tests/modules/virtualbox/test_virtualbox_manager.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gns3server/modules/virtualbox/__init__.py b/gns3server/modules/virtualbox/__init__.py index 509b7182..890ad6e7 100644 --- a/gns3server/modules/virtualbox/__init__.py +++ b/gns3server/modules/virtualbox/__init__.py @@ -168,7 +168,7 @@ class VirtualBox(BaseManager): vms = [] result = yield from self.execute("list", ["vms"]) 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 vmname, _ = line.rsplit(' ', 1) vmname = vmname.strip('"') diff --git a/tests/modules/virtualbox/test_virtualbox_manager.py b/tests/modules/virtualbox/test_virtualbox_manager.py index 54289554..caae45e3 100644 --- a/tests/modules/virtualbox/test_virtualbox_manager.py +++ b/tests/modules/virtualbox/test_virtualbox_manager.py @@ -75,9 +75,9 @@ def test_get_list(manager, loop): vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', '"Carriage', 'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}', + '', '"" {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}'] @asyncio.coroutine def execute_mock(cmd, args): From 78891ae00e21149c7d461e2893c27cd0be178260 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Wed, 3 Jun 2015 15:38:34 +0200 Subject: [PATCH 14/14] Basic Auth support --- gns3server/web/route.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/gns3server/web/route.py b/gns3server/web/route.py index f376b820..8cf70b0b 100644 --- a/gns3server/web/route.py +++ b/gns3server/web/route.py @@ -83,6 +83,28 @@ class Route(object): def delete(cls, 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 def _route(cls, method, path, *args, **kw): # This block is executed only the first time @@ -118,6 +140,13 @@ class Route(object): def control_schema(request): # 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 if api_version is None: response = Response(request=request, route=route, output_schema=output_schema) @@ -127,7 +156,6 @@ class Route(object): # API call try: request = yield from parse_request(request, input_schema) - server_config = Config.instance().get_section_config("Server") record_file = server_config.get("record") if record_file: try: