diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py
index 9f824c16..8d2587c9 100644
--- a/gns3server/handlers/__init__.py
+++ b/gns3server/handlers/__init__.py
@@ -16,4 +16,3 @@
from gns3server.handlers.api import *
from gns3server.handlers.upload_handler import UploadHandler
-
diff --git a/gns3server/handlers/api/dynamips_vm_handler.py b/gns3server/handlers/api/dynamips_vm_handler.py
index c2855a29..79e7c4c0 100644
--- a/gns3server/handlers/api/dynamips_vm_handler.py
+++ b/gns3server/handlers/api/dynamips_vm_handler.py
@@ -30,6 +30,7 @@ from ...schemas.dynamips_vm import VM_CONFIGS_SCHEMA
from ...modules.dynamips import Dynamips
from ...modules.project_manager import ProjectManager
+
class DynamipsVMHandler:
"""
@@ -387,4 +388,3 @@ class DynamipsVMHandler:
idlepc = yield from dynamips_manager.auto_idlepc(vm)
response.set_status(200)
response.json({"idlepc": idlepc})
-
diff --git a/gns3server/handlers/upload_handler.py b/gns3server/handlers/upload_handler.py
index 7bc6a5b7..2f5d9fd4 100644
--- a/gns3server/handlers/upload_handler.py
+++ b/gns3server/handlers/upload_handler.py
@@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import os
+import stat
+
+from ..config import Config
from ..web.route import Route
from ..schemas.version import VERSION_SCHEMA
from ..version import __version__
@@ -30,5 +34,38 @@ class UploadHandler:
api_version=None
)
def index(request, response):
- response.template("upload.html")
+ files = []
+ for filename in os.listdir(UploadHandler.image_directory()):
+ if os.path.isfile(os.path.join(UploadHandler.image_directory(), filename)):
+ if filename[0] != ".":
+ files.append(filename)
+ response.template("upload.html", files=files, image_path=UploadHandler.image_directory())
+ @classmethod
+ @Route.post(
+ r"/upload",
+ description="Manage upload of GNS3 images",
+ api_version=None
+ )
+ def upload(request, response):
+ data = yield from request.post()
+
+ destination_path = os.path.join(UploadHandler.image_directory(), data["file"].filename)
+
+ try:
+ os.makedirs(UploadHandler.image_directory(), exist_ok=True)
+ with open(destination_path, "wb+") as f:
+ f.write(data["file"].file.read())
+ print(destination_path)
+ st = os.stat(destination_path)
+ os.chmod(destination_path, st.st_mode | stat.S_IXUSR)
+ except OSError as e:
+ response.html("Could not upload file: {}".format(e))
+ response.set_status(500)
+ return
+ response.redirect("/upload")
+
+ @staticmethod
+ def image_directory():
+ server_config = Config.instance().get_section_config("Server")
+ return os.path.expanduser(server_config.get("image_directory", "~/GNS3/images"))
diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py
index 5c388f14..d8f70ab6 100644
--- a/gns3server/modules/dynamips/__init__.py
+++ b/gns3server/modules/dynamips/__init__.py
@@ -357,10 +357,10 @@ class Dynamips(BaseManager):
raise DynamipsError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
# check if we have an allocated NIO UDP auto
#nio = node.hypervisor.get_nio_udp_auto(lport)
- #if not nio:
+ # if not nio:
# otherwise create an NIO UDP
nio = NIOUDP(node.hypervisor, lport, rhost, rport)
- #else:
+ # else:
# nio.connect(rhost, rport)
elif nio_settings["type"] == "nio_generic_ethernet":
ethernet_device = nio_settings["ethernet_device"]
@@ -462,9 +462,9 @@ class Dynamips(BaseManager):
slot_id = int(name[-1])
adapter_name = value
adapter = ADAPTER_MATRIX[adapter_name]()
- if vm.slots[slot_id] and type(vm.slots[slot_id]) != type(adapter):
+ if vm.slots[slot_id] and not isinstance(vm.slots[slot_id], type(adapter)):
yield from vm.slot_remove_binding(slot_id)
- if type(vm.slots[slot_id]) != type(adapter):
+ if not isinstance(vm.slots[slot_id], type(adapter)):
yield from vm.slot_add_binding(slot_id, adapter)
elif name.startswith("slot") and value is None:
slot_id = int(name[-1])
@@ -474,9 +474,9 @@ class Dynamips(BaseManager):
wic_slot_id = int(name[-1])
wic_name = value
wic = WIC_MATRIX[wic_name]()
- if vm.slots[0].wics[wic_slot_id] and type(vm.slots[0].wics[wic_slot_id]) != type(wic):
+ if vm.slots[0].wics[wic_slot_id] and not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
yield from vm.uninstall_wic(wic_slot_id)
- if type(vm.slots[0].wics[wic_slot_id]) != type(wic):
+ if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
yield from vm.install_wic(wic_slot_id, wic)
elif name.startswith("wic") and value is None:
wic_slot_id = int(name[-1])
diff --git a/gns3server/modules/dynamips/nodes/atm_switch.py b/gns3server/modules/dynamips/nodes/atm_switch.py
index 32867883..5c620221 100644
--- a/gns3server/modules/dynamips/nodes/atm_switch.py
+++ b/gns3server/modules/dynamips/nodes/atm_switch.py
@@ -31,6 +31,7 @@ log = logging.getLogger(__name__)
class ATMSwitch(Device):
+
"""
Dynamips ATM switch.
@@ -79,7 +80,6 @@ class ATMSwitch(Device):
new_name=new_name))
self._name = new_name
-
@property
def nios(self):
"""
diff --git a/gns3server/modules/dynamips/nodes/bridge.py b/gns3server/modules/dynamips/nodes/bridge.py
index 5f056101..174fbb86 100644
--- a/gns3server/modules/dynamips/nodes/bridge.py
+++ b/gns3server/modules/dynamips/nodes/bridge.py
@@ -25,6 +25,7 @@ from .device import Device
class Bridge(Device):
+
"""
Dynamips bridge.
diff --git a/gns3server/modules/dynamips/nodes/device.py b/gns3server/modules/dynamips/nodes/device.py
index cbf755bd..fb5b3595 100644
--- a/gns3server/modules/dynamips/nodes/device.py
+++ b/gns3server/modules/dynamips/nodes/device.py
@@ -17,6 +17,7 @@
class Device:
+
"""
Base device for switches and hubs
diff --git a/gns3server/modules/dynamips/nodes/ethernet_hub.py b/gns3server/modules/dynamips/nodes/ethernet_hub.py
index 33c523ab..39a891ab 100644
--- a/gns3server/modules/dynamips/nodes/ethernet_hub.py
+++ b/gns3server/modules/dynamips/nodes/ethernet_hub.py
@@ -29,6 +29,7 @@ log = logging.getLogger(__name__)
class EthernetHub(Bridge):
+
"""
Dynamips Ethernet hub (based on Bridge)
diff --git a/gns3server/modules/dynamips/nodes/ethernet_switch.py b/gns3server/modules/dynamips/nodes/ethernet_switch.py
index b0e425b8..7a8d8abe 100644
--- a/gns3server/modules/dynamips/nodes/ethernet_switch.py
+++ b/gns3server/modules/dynamips/nodes/ethernet_switch.py
@@ -31,6 +31,7 @@ log = logging.getLogger(__name__)
class EthernetSwitch(Device):
+
"""
Dynamips Ethernet switch.
diff --git a/gns3server/modules/dynamips/nodes/frame_relay_switch.py b/gns3server/modules/dynamips/nodes/frame_relay_switch.py
index 16572871..74fdda1c 100644
--- a/gns3server/modules/dynamips/nodes/frame_relay_switch.py
+++ b/gns3server/modules/dynamips/nodes/frame_relay_switch.py
@@ -30,6 +30,7 @@ log = logging.getLogger(__name__)
class FrameRelaySwitch(Device):
+
"""
Dynamips Frame Relay switch.
diff --git a/gns3server/schemas/dynamips_device.py b/gns3server/schemas/dynamips_device.py
index 28ba32be..52a9ba53 100644
--- a/gns3server/schemas/dynamips_device.py
+++ b/gns3server/schemas/dynamips_device.py
@@ -63,7 +63,7 @@ DEVICE_UPDATE_SCHEMA = {
"vlan": {"description": "VLAN number",
"type": "integer",
"minimum": 1
- },
+ },
},
"required": ["port", "type", "vlan"],
"additionalProperties": False
@@ -108,7 +108,7 @@ DEVICE_OBJECT_SCHEMA = {
"vlan": {"description": "VLAN number",
"type": "integer",
"minimum": 1
- },
+ },
},
"required": ["port", "type", "vlan"],
"additionalProperties": False
@@ -306,7 +306,7 @@ DEVICE_NIO_SCHEMA = {
"vlan": {"description": "VLAN number",
"type": "integer",
"minimum": 1
- },
+ },
},
"required": ["type", "vlan"],
"additionalProperties": False
diff --git a/gns3server/templates/upload.html b/gns3server/templates/upload.html
index c912e49d..e9ba8b0b 100644
--- a/gns3server/templates/upload.html
+++ b/gns3server/templates/upload.html
@@ -7,10 +7,10 @@
- {%if items%}
-
Files on {{host}}
- {%for item in items%}
- {{path}}/{{item}}
+ {%if files%}
+ Files on {{gns3_host}}
+ {%for file in files%}
+ {{image_path}}/{{file}}
{%endfor%}
{%endif%}
{% endblock %}
diff --git a/gns3server/web/response.py b/gns3server/web/response.py
index 9bd453c6..42112fa9 100644
--- a/gns3server/web/response.py
+++ b/gns3server/web/response.py
@@ -46,7 +46,7 @@ class Response(aiohttp.web.Response):
log.debug("%s", request.json)
log.info("Response: %d %s", self.status, self.reason)
log.debug(dict(self.headers))
- if hasattr(self, 'body') and self.body is not None:
+ if hasattr(self, 'body') and self.body is not None and self.headers["CONTENT-TYPE"] == "application/json":
log.debug(json.loads(self.body.decode('utf-8')))
return super().start(request)
@@ -90,3 +90,11 @@ class Response(aiohttp.web.Response):
log.error("Invalid output query. JSON schema error: {}".format(e.message))
raise aiohttp.web.HTTPBadRequest(text="{}".format(e))
self.body = json.dumps(answer, indent=4, sort_keys=True).encode('utf-8')
+
+ def redirect(self, url):
+ """
+ Redirect to url
+
+ :params url: Redirection URL
+ """
+ raise aiohttp.web.HTTPFound(url)
diff --git a/gns3server/web/route.py b/gns3server/web/route.py
index 4ec4d2b8..93b10eed 100644
--- a/gns3server/web/route.py
+++ b/gns3server/web/route.py
@@ -82,6 +82,8 @@ class Route(object):
output_schema = kw.get("output", {})
input_schema = kw.get("input", {})
api_version = kw.get("api_version", 1)
+
+ # If it's a JSON api endpoint just register the endpoint an do nothing
if api_version is None:
cls._path = path
else:
@@ -107,6 +109,14 @@ class Route(object):
@asyncio.coroutine
def control_schema(request):
# This block is executed at each method call
+
+ # Non API call
+ if api_version is None:
+ response = Response(route=route, output_schema=output_schema)
+ yield from func(request, response)
+ return response
+
+ # API call
try:
request = yield from parse_request(request, input_schema)
response = Response(route=route, output_schema=output_schema)
diff --git a/tests/handlers/api/base.py b/tests/handlers/api/base.py
index d0f38878..d7c7f690 100644
--- a/tests/handlers/api/base.py
+++ b/tests/handlers/api/base.py
@@ -49,7 +49,7 @@ class Query:
return "http://{}:{}{}".format(self._host, self._port, path)
return "http://{}:{}/v{}{}".format(self._host, self._port, version, path)
- def _fetch(self, method, path, body=None, api_version = 1, **kwargs):
+ def _fetch(self, method, path, body=None, api_version=1, **kwargs):
"""Fetch an url, parse the JSON and return response
Options:
diff --git a/tests/handlers/test_upload.py b/tests/handlers/test_upload.py
index d53b6302..65869b4f 100644
--- a/tests/handlers/test_upload.py
+++ b/tests/handlers/test_upload.py
@@ -15,17 +15,33 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-"""
-This test suite check /version endpoint
-It's also used for unittest the HTTP implementation.
-"""
+
+import aiohttp
+import os
+from unittest.mock import patch
from gns3server.version import __version__
-def test_version_index_upload(server):
+def test_index_upload(server):
response = server.get('/upload', api_version=None)
assert response.status == 200
html = response.html
assert "GNS3 Server" in html
assert "Select & Upload" in html
+
+
+def test_upload(server, tmpdir):
+
+ with open(str(tmpdir / "test"), "w+") as f:
+ f.write("TEST")
+ body = aiohttp.FormData()
+ body.add_field("file", open(str(tmpdir / "test"), "rb"), content_type="application/iou", filename="test2")
+
+ with patch("gns3server.config.Config.get_section_config", return_value={"image_directory": str(tmpdir)}):
+ response = server.post('/upload', api_version=None, body=body, raw=True)
+
+ with open(str(tmpdir / "test2")) as f:
+ assert f.read() == "TEST"
+
+ assert "test2" in response.body.decode("utf-8")