diff --git a/docs/glossary.rst b/docs/glossary.rst
index 79d1c49d..2d801aa0 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -37,3 +37,7 @@ Compute
The process running on each server with GNS3. The GNS3 compute node
is controlled by the GNS3 controller.
+
+Symbol
+------
+Symbol are the icon used for nodes.
diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py
index 4c22742d..d520eb12 100644
--- a/gns3server/controller/__init__.py
+++ b/gns3server/controller/__init__.py
@@ -25,6 +25,7 @@ from ..config import Config
from .project import Project
from .compute import Compute
from .notification import Notification
+from .symbols import Symbols
from ..version import __version__
from .topology import load_topology
@@ -39,6 +40,7 @@ class Controller:
self._computes = {}
self._projects = {}
self._notification = Notification(self)
+ self.symbols = Symbols()
if sys.platform.startswith("win"):
config_path = os.path.join(os.path.expandvars("%APPDATA%"), "GNS3")
diff --git a/gns3server/controller/symbols.py b/gns3server/controller/symbols.py
new file mode 100644
index 00000000..5e787fa7
--- /dev/null
+++ b/gns3server/controller/symbols.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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 .
+
+import os
+
+
+from ..utils.get_resource import get_resource
+
+
+class Symbols:
+ """
+ Manage GNS3 symbols
+ """
+
+ def __init__(self):
+ self.list()
+
+ def list(self):
+ self._symbols_path = {}
+ symbols = []
+ for file in os.listdir(get_resource("symbols")):
+ if file.startswith('.'):
+ continue
+ symbol_id = ':/symbols/' + file
+ symbols.append({
+ 'symbol_id': symbol_id,
+ 'filename': file,
+ 'builtin': True,
+ 'url': '/static/builtin_symbols/' + file
+ })
+ self._symbols_path[symbol_id] = os.path.join(get_resource("symbols"), file)
+ #TODO: support ~/GNS3/symbols directory
+ return symbols
+
+ def get_path(self, symbol_id):
+ return self._symbols_path[symbol_id]
diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py
index 557685f2..51b9fc07 100644
--- a/gns3server/handlers/__init__.py
+++ b/gns3server/handlers/__init__.py
@@ -16,6 +16,8 @@
from gns3server.handlers.index_handler import IndexHandler
+from gns3server.handlers.static_handler import StaticHandler
+
from gns3server.handlers.api.controller import *
from gns3server.handlers.api.compute import *
diff --git a/gns3server/handlers/api/controller/__init__.py b/gns3server/handlers/api/controller/__init__.py
index ef0de94c..abf7e6c4 100644
--- a/gns3server/handlers/api/controller/__init__.py
+++ b/gns3server/handlers/api/controller/__init__.py
@@ -21,3 +21,4 @@ from .node_handler import NodeHandler
from .link_handler import LinkHandler
from .server_handler import ServerHandler
from .drawing_handler import DrawingHandler
+from .symbol_handler import SymbolHandler
diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py
new file mode 100644
index 00000000..1f5ddc6f
--- /dev/null
+++ b/gns3server/handlers/api/controller/symbol_handler.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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 .
+
+from gns3server.web.route import Route
+from gns3server.controller import Controller
+
+
+import logging
+log = logging.getLogger(__name__)
+
+
+class SymbolHandler:
+ """API entry points for symbols management."""
+
+ @Route.get(
+ r"/symbols",
+ description="List of symbols",
+ status_codes={
+ 200: "Symbols list returned"
+ })
+ def list(request, response):
+
+ controller = Controller.instance()
+ response.json(controller.symbols.list())
diff --git a/gns3server/handlers/static_handler.py b/gns3server/handlers/static_handler.py
new file mode 100644
index 00000000..c8e09b48
--- /dev/null
+++ b/gns3server/handlers/static_handler.py
@@ -0,0 +1,66 @@
+#
+# Copyright (C) 2016 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 .
+
+import os
+import asyncio
+import mimetypes
+from aiohttp import hdrs
+
+from gns3server.web.route import Route
+from gns3server.utils.get_resource import get_resource
+
+
+class StaticHandler:
+
+ @Route.get(
+ r"/static/{type}/{path:.+}",
+ description="Serve static content from various locations"
+ )
+ def get(request, response):
+ type = request.match_info["type"]
+ # CLeanup the path for security
+ path = os.path.normpath(request.match_info["path"]).strip('/.')
+ if type == "builtin_symbols":
+ try:
+ yield from StaticHandler._serve_file(os.path.join(get_resource("symbols"), path), request, response)
+ except OSError:
+ response.set_status(404)
+
+ @asyncio.coroutine
+ def _serve_file(path, request, response):
+ ct, encoding = mimetypes.guess_type(path)
+ if not ct:
+ ct = 'application/octet-stream'
+ if encoding:
+ response.headers[hdrs.CONTENT_ENCODING] = encoding
+ response.content_type = ct
+
+ st = os.stat(path)
+ response.last_modified = st.st_mtime
+ response.content_length = st.st_size
+
+ with open(path, 'rb') as fobj:
+ response.start(request)
+ chunk_size = 4096
+ chunk = fobj.read(chunk_size)
+ while chunk:
+ response.write(chunk)
+ yield from response.drain()
+ chunk = fobj.read(chunk_size)
+
+ if chunk:
+ response.write(chunk[:count])
+ yield from response.drain()
diff --git a/gns3server/symbols/PBX.svg b/gns3server/symbols/PBX.svg
new file mode 100755
index 00000000..40528c72
--- /dev/null
+++ b/gns3server/symbols/PBX.svg
@@ -0,0 +1,304 @@
+
+
+
diff --git a/gns3server/symbols/PIX_firewall.svg b/gns3server/symbols/PIX_firewall.svg
new file mode 100755
index 00000000..257d9d27
--- /dev/null
+++ b/gns3server/symbols/PIX_firewall.svg
@@ -0,0 +1,187 @@
+
+
+
diff --git a/gns3server/symbols/access_point.svg b/gns3server/symbols/access_point.svg
new file mode 100755
index 00000000..ea2475ed
--- /dev/null
+++ b/gns3server/symbols/access_point.svg
@@ -0,0 +1,516 @@
+
+
+
diff --git a/gns3server/symbols/access_server.svg b/gns3server/symbols/access_server.svg
new file mode 100755
index 00000000..a904702c
--- /dev/null
+++ b/gns3server/symbols/access_server.svg
@@ -0,0 +1,270 @@
+
+
+
diff --git a/gns3server/symbols/asa.svg b/gns3server/symbols/asa.svg
new file mode 100755
index 00000000..8a8067c5
--- /dev/null
+++ b/gns3server/symbols/asa.svg
@@ -0,0 +1,424 @@
+
+
+
+
diff --git a/gns3server/symbols/atm_bridge.svg b/gns3server/symbols/atm_bridge.svg
new file mode 100755
index 00000000..f3aa984b
--- /dev/null
+++ b/gns3server/symbols/atm_bridge.svg
@@ -0,0 +1,219 @@
+
+
+
diff --git a/gns3server/symbols/atm_switch.svg b/gns3server/symbols/atm_switch.svg
new file mode 100755
index 00000000..ff9605ef
--- /dev/null
+++ b/gns3server/symbols/atm_switch.svg
@@ -0,0 +1,243 @@
+
+
+
diff --git a/gns3server/symbols/call_manager.svg b/gns3server/symbols/call_manager.svg
new file mode 100755
index 00000000..9262ba82
--- /dev/null
+++ b/gns3server/symbols/call_manager.svg
@@ -0,0 +1,228 @@
+
+
+
diff --git a/gns3server/symbols/cloud.svg b/gns3server/symbols/cloud.svg
new file mode 100755
index 00000000..a1bc02cc
--- /dev/null
+++ b/gns3server/symbols/cloud.svg
@@ -0,0 +1,163 @@
+
+
+
+
diff --git a/gns3server/symbols/computer.svg b/gns3server/symbols/computer.svg
new file mode 100755
index 00000000..36c31a3a
--- /dev/null
+++ b/gns3server/symbols/computer.svg
@@ -0,0 +1,560 @@
+
+
+
diff --git a/gns3server/symbols/docker_guest.svg b/gns3server/symbols/docker_guest.svg
new file mode 100755
index 00000000..15753d28
--- /dev/null
+++ b/gns3server/symbols/docker_guest.svg
@@ -0,0 +1,657 @@
+
+
+
+
diff --git a/gns3server/symbols/dslam.svg b/gns3server/symbols/dslam.svg
new file mode 100755
index 00000000..5cd67b31
--- /dev/null
+++ b/gns3server/symbols/dslam.svg
@@ -0,0 +1,155 @@
+
+
+
+
diff --git a/gns3server/symbols/edge_label_switch_router.svg b/gns3server/symbols/edge_label_switch_router.svg
new file mode 100755
index 00000000..3cc4753d
--- /dev/null
+++ b/gns3server/symbols/edge_label_switch_router.svg
@@ -0,0 +1,216 @@
+
+
+
diff --git a/gns3server/symbols/ethernet_switch.svg b/gns3server/symbols/ethernet_switch.svg
new file mode 100755
index 00000000..24475c5a
--- /dev/null
+++ b/gns3server/symbols/ethernet_switch.svg
@@ -0,0 +1,228 @@
+
+
+
diff --git a/gns3server/symbols/firewall.svg b/gns3server/symbols/firewall.svg
new file mode 100755
index 00000000..88898ab7
--- /dev/null
+++ b/gns3server/symbols/firewall.svg
@@ -0,0 +1,187 @@
+
+
+
diff --git a/gns3server/symbols/frame_relay_switch.svg b/gns3server/symbols/frame_relay_switch.svg
new file mode 100755
index 00000000..00ba4f02
--- /dev/null
+++ b/gns3server/symbols/frame_relay_switch.svg
@@ -0,0 +1,263 @@
+
+
+
diff --git a/gns3server/symbols/gateway.svg b/gns3server/symbols/gateway.svg
new file mode 100755
index 00000000..33feca4f
--- /dev/null
+++ b/gns3server/symbols/gateway.svg
@@ -0,0 +1,309 @@
+
+
+
diff --git a/gns3server/symbols/hub.svg b/gns3server/symbols/hub.svg
new file mode 100755
index 00000000..322dcb2d
--- /dev/null
+++ b/gns3server/symbols/hub.svg
@@ -0,0 +1,204 @@
+
+
+
diff --git a/gns3server/symbols/ids.svg b/gns3server/symbols/ids.svg
new file mode 100755
index 00000000..19ab9b3a
--- /dev/null
+++ b/gns3server/symbols/ids.svg
@@ -0,0 +1,310 @@
+
+
+
+
diff --git a/gns3server/symbols/iosv_l2_virl.svg b/gns3server/symbols/iosv_l2_virl.svg
new file mode 100755
index 00000000..efdf62f0
--- /dev/null
+++ b/gns3server/symbols/iosv_l2_virl.svg
@@ -0,0 +1,286 @@
+
+
+
+
diff --git a/gns3server/symbols/iosv_virl.svg b/gns3server/symbols/iosv_virl.svg
new file mode 100755
index 00000000..7914f1df
--- /dev/null
+++ b/gns3server/symbols/iosv_virl.svg
@@ -0,0 +1,394 @@
+
+
+
+
diff --git a/gns3server/symbols/ip_phone.svg b/gns3server/symbols/ip_phone.svg
new file mode 100755
index 00000000..ca9cfcf8
--- /dev/null
+++ b/gns3server/symbols/ip_phone.svg
@@ -0,0 +1,171 @@
+
+
+
+
diff --git a/gns3server/symbols/label_switch_router.svg b/gns3server/symbols/label_switch_router.svg
new file mode 100755
index 00000000..4946556b
--- /dev/null
+++ b/gns3server/symbols/label_switch_router.svg
@@ -0,0 +1,223 @@
+
+
+
diff --git a/gns3server/symbols/lightweight_ap.svg b/gns3server/symbols/lightweight_ap.svg
new file mode 100755
index 00000000..12248041
--- /dev/null
+++ b/gns3server/symbols/lightweight_ap.svg
@@ -0,0 +1,536 @@
+
+
+
diff --git a/gns3server/symbols/multilayer_switch.svg b/gns3server/symbols/multilayer_switch.svg
new file mode 100755
index 00000000..396f653f
--- /dev/null
+++ b/gns3server/symbols/multilayer_switch.svg
@@ -0,0 +1,317 @@
+
+
+
diff --git a/gns3server/symbols/optical_router.svg b/gns3server/symbols/optical_router.svg
new file mode 100755
index 00000000..a3b390f0
--- /dev/null
+++ b/gns3server/symbols/optical_router.svg
@@ -0,0 +1,207 @@
+
+
+
diff --git a/gns3server/symbols/printer.svg b/gns3server/symbols/printer.svg
new file mode 100755
index 00000000..b0053b9c
--- /dev/null
+++ b/gns3server/symbols/printer.svg
@@ -0,0 +1,178 @@
+
+
+
diff --git a/gns3server/symbols/qemu_guest.svg b/gns3server/symbols/qemu_guest.svg
new file mode 100755
index 00000000..a060ac03
--- /dev/null
+++ b/gns3server/symbols/qemu_guest.svg
@@ -0,0 +1,1387 @@
+
+
+
+
diff --git a/gns3server/symbols/route_switch_processor.svg b/gns3server/symbols/route_switch_processor.svg
new file mode 100755
index 00000000..b1b49874
--- /dev/null
+++ b/gns3server/symbols/route_switch_processor.svg
@@ -0,0 +1,395 @@
+
+
+
diff --git a/gns3server/symbols/router.awp.svg b/gns3server/symbols/router.awp.svg
new file mode 100644
index 00000000..0c4bbc23
--- /dev/null
+++ b/gns3server/symbols/router.awp.svg
@@ -0,0 +1,362 @@
+
+
+
+
diff --git a/gns3server/symbols/router.svg b/gns3server/symbols/router.svg
new file mode 100755
index 00000000..5bffea3e
--- /dev/null
+++ b/gns3server/symbols/router.svg
@@ -0,0 +1,203 @@
+
+
+
diff --git a/gns3server/symbols/router_firewall.svg b/gns3server/symbols/router_firewall.svg
new file mode 100755
index 00000000..b4ca0658
--- /dev/null
+++ b/gns3server/symbols/router_firewall.svg
@@ -0,0 +1,395 @@
+
+
+
diff --git a/gns3server/symbols/router_netflow.svg b/gns3server/symbols/router_netflow.svg
new file mode 100755
index 00000000..8a9f0be5
--- /dev/null
+++ b/gns3server/symbols/router_netflow.svg
@@ -0,0 +1,215 @@
+
+
+
diff --git a/gns3server/symbols/server.svg b/gns3server/symbols/server.svg
new file mode 100755
index 00000000..0392ef3f
--- /dev/null
+++ b/gns3server/symbols/server.svg
@@ -0,0 +1,226 @@
+
+
+
diff --git a/gns3server/symbols/sip_server.svg b/gns3server/symbols/sip_server.svg
new file mode 100755
index 00000000..f0e94d60
--- /dev/null
+++ b/gns3server/symbols/sip_server.svg
@@ -0,0 +1,305 @@
+
+
+
+
diff --git a/gns3server/symbols/vbox_guest.svg b/gns3server/symbols/vbox_guest.svg
new file mode 100755
index 00000000..41568c0a
--- /dev/null
+++ b/gns3server/symbols/vbox_guest.svg
@@ -0,0 +1,7691 @@
+
+
+
+
diff --git a/gns3server/symbols/vmware_guest.svg b/gns3server/symbols/vmware_guest.svg
new file mode 100755
index 00000000..8f0b75a7
--- /dev/null
+++ b/gns3server/symbols/vmware_guest.svg
@@ -0,0 +1,572 @@
+
+
+
+
diff --git a/gns3server/symbols/voice_access_server.svg b/gns3server/symbols/voice_access_server.svg
new file mode 100755
index 00000000..f38c4f23
--- /dev/null
+++ b/gns3server/symbols/voice_access_server.svg
@@ -0,0 +1,289 @@
+
+
+
+
diff --git a/gns3server/symbols/voice_router.svg b/gns3server/symbols/voice_router.svg
new file mode 100755
index 00000000..f47445da
--- /dev/null
+++ b/gns3server/symbols/voice_router.svg
@@ -0,0 +1,223 @@
+
+
+
+
diff --git a/gns3server/symbols/vpcs_guest.svg b/gns3server/symbols/vpcs_guest.svg
new file mode 100644
index 00000000..05cc92de
--- /dev/null
+++ b/gns3server/symbols/vpcs_guest.svg
@@ -0,0 +1,574 @@
+
+
+
+
diff --git a/gns3server/symbols/wlan_controller.svg b/gns3server/symbols/wlan_controller.svg
new file mode 100755
index 00000000..f4495749
--- /dev/null
+++ b/gns3server/symbols/wlan_controller.svg
@@ -0,0 +1,528 @@
+
+
+
diff --git a/tests/controller/test_symbols.py b/tests/controller/test_symbols.py
new file mode 100644
index 00000000..94ef315d
--- /dev/null
+++ b/tests/controller/test_symbols.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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 .
+
+import os
+
+
+from gns3server.controller.symbols import Symbols
+from gns3server.utils.get_resource import get_resource
+
+
+def test_list():
+ symbols = Symbols()
+ assert {
+ 'symbol_id': ':/symbols/firewall.svg',
+ 'url': '/static/builtin_symbols/firewall.svg',
+ 'filename': 'firewall.svg',
+ 'builtin': True
+ } in symbols.list()
+ assert symbols
diff --git a/tests/handlers/api/controller/test_symbol.py b/tests/handlers/api/controller/test_symbol.py
new file mode 100644
index 00000000..23c8d483
--- /dev/null
+++ b/tests/handlers/api/controller/test_symbol.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015 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 .
+
+from gns3server.config import Config
+
+
+def test_symbols(http_controller):
+ response = http_controller.get('/symbols', example=True)
+ assert response.status == 200
+ assert {
+ 'symbol_id': ':/symbols/firewall.svg',
+ 'url': '/static/builtin_symbols/firewall.svg',
+ 'filename': 'firewall.svg',
+ 'builtin': True
+ } in response.json
diff --git a/tests/handlers/test_static.py b/tests/handlers/test_static.py
new file mode 100644
index 00000000..4ae8746f
--- /dev/null
+++ b/tests/handlers/test_static.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015 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 .
+
+
+import aiohttp
+import os
+from unittest.mock import patch
+
+
+def test_get(http_root):
+ response = http_root.get('/static/builtin_symbols/firewall.svg')
+ assert response.status == 200
+ assert response.headers['CONTENT-LENGTH'] == '9381'
+ assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
+ assert '' in response.html
+
+ response = http_root.get('/static/builtin_symbols/../main.py')
+ assert response.status == 404
+
+ response = http_root.get('/static/builtin_symbols/404.png')
+ assert response.status == 404