diff --git a/docs/general.rst b/docs/general.rst
index eef976fd..cc2ff848 100644
--- a/docs/general.rst
+++ b/docs/general.rst
@@ -202,14 +202,14 @@ upload and run code on your machine.
Notifications
=============
-You can receive notification from the server if you listen the HTTP stream /notifications.
+You can receive notification from the server if you listen the HTTP stream /notifications or the websocket.
The available notification are:
-* ping
-* vm.created
-* vm.started
-* vm.stopped
-* log.error
+ * ping
+ * vm.created
+ * vm.started
+ * vm.stopped
+ * log.error
Previous versions
=================
diff --git a/gns3server/handlers/api/hypervisor/__init__.py b/gns3server/handlers/api/hypervisor/__init__.py
index 5e1ff7ed..f0a74b38 100644
--- a/gns3server/handlers/api/hypervisor/__init__.py
+++ b/gns3server/handlers/api/hypervisor/__init__.py
@@ -29,6 +29,7 @@ from .vmware_handler import VMwareHandler
from .config_handler import ConfigHandler
from .file_handler import FileHandler
from .version_handler import VersionHandler
+from .notification_handler import NotificationHandler
if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test") or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1":
diff --git a/gns3server/handlers/api/hypervisor/notification_handler.py b/gns3server/handlers/api/hypervisor/notification_handler.py
new file mode 100644
index 00000000..09185826
--- /dev/null
+++ b/gns3server/handlers/api/hypervisor/notification_handler.py
@@ -0,0 +1,41 @@
+# -*- 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 asyncio
+
+from ....web.route import Route
+from ....hypervisor.notification_manager import NotificationManager
+from aiohttp.web import WebSocketResponse
+
+
+class NotificationHandler:
+
+ @classmethod
+ @Route.get(
+ r"/notifications/ws",
+ description="Send notifications about what happend using websockets")
+ def notifications(request, response):
+ notifications = NotificationManager.instance()
+ ws = WebSocketResponse()
+ yield from ws.prepare(request)
+
+ with notifications.queue() as queue:
+ while True:
+ notif = yield from queue.get_json(5)
+ ws.send_str(notif)
+ return ws
+
diff --git a/gns3server/hypervisor/notification_manager.py b/gns3server/hypervisor/notification_manager.py
new file mode 100644
index 00000000..ba65f5b4
--- /dev/null
+++ b/gns3server/hypervisor/notification_manager.py
@@ -0,0 +1,118 @@
+#!/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 asyncio
+import psutil
+import json
+from contextlib import contextmanager
+
+
+class NotificationQueue(asyncio.Queue):
+ """
+ Queue returned by the notification manager.
+ """
+ def __init__(self):
+ super().__init__()
+ self._first = True
+
+ @asyncio.coroutine
+ def get(self, timeout):
+ """
+ When timeout is expire we send a ping notification with server informations
+ """
+
+ # At first get we return a ping so the client receive immediately data
+ if self._first:
+ self._first = False
+ return ("ping", self._getPing(), {})
+
+ try:
+ (action, msg, kwargs) = yield from asyncio.wait_for(super().get(), timeout)
+ except asyncio.futures.TimeoutError:
+ return ("ping", self._getPing(), {})
+ return (action, msg, kwargs)
+
+ def _getPing(self):
+ """
+ Return the content of the ping notification
+ """
+ msg = {}
+ # Non blocking call in order to get cpu usage. First call will return 0
+ msg["cpu_usage_percent"] = psutil.cpu_percent(interval=None)
+ msg["memory_usage_percent"] = psutil.virtual_memory().percent
+ return msg
+
+ @asyncio.coroutine
+ def get_json(self, timeout):
+ """
+ Get a message as a JSON
+ """
+ (action, msg, kwargs) = yield from self.get(timeout)
+ if hasattr(msg, "__json__"):
+ msg = {"action": action, "event": msg.__json__()}
+ else:
+ msg = {"action": action, "event": msg}
+ msg.update(kwargs)
+ return json.dumps(msg, sort_keys=True)
+
+
+class NotificationManager:
+ """
+ Manage the notification queue where the controller
+ will connect to get notifications from hypervisors
+ """
+
+ def __init__(self):
+ self._listeners = set()
+
+ @contextmanager
+ def queue(self):
+ """
+ Get a queue of notifications
+
+ Use it with Python with
+ """
+ queue = NotificationQueue()
+ self._listeners.add(queue)
+ yield queue
+ self._listeners.remove(queue)
+
+ def emit(self, action, event, **kwargs):
+ """
+ Send an event to all the client listening for notifications
+
+ :param action: Action name
+ :param event: Event to send
+ :param kwargs: Add this meta to the notif (project_id for example)
+ """
+ for listener in self._listeners:
+ listener.put_nowait((action, event, kwargs))
+
+ @staticmethod
+ def reset():
+ NotificationManager._instance = None
+
+ @staticmethod
+ def instance():
+ """
+ Singleton to return only on instance of NotificationManager.
+ :returns: instance of NotificationManager
+ """
+
+ if not hasattr(NotificationManager, '_instance') or NotificationManager._instance is None:
+ NotificationManager._instance = NotificationManager()
+ return NotificationManager._instance
diff --git a/gns3server/hypervisor/project.py b/gns3server/hypervisor/project.py
index 322512ac..e548b94e 100644
--- a/gns3server/hypervisor/project.py
+++ b/gns3server/hypervisor/project.py
@@ -23,6 +23,7 @@ import hashlib
from uuid import UUID, uuid4
from .port_manager import PortManager
+from .notification_manager import NotificationManager
from ..config import Config
from ..utils.asyncio import wait_run_in_executor
@@ -56,9 +57,6 @@ class Project:
self._used_tcp_ports = set()
self._used_udp_ports = set()
- # clients listening for notifications
- self._listeners = set()
-
if path is None:
location = self._config().get("project_directory", self._get_default_project_directory())
path = os.path.join(location, self._id)
@@ -422,28 +420,7 @@ class Project:
:param action: Action name
:param event: Event to send
"""
- for listener in self._listeners:
- listener.put_nowait((action, event, ))
-
- def get_listen_queue(self):
- """Get a queue where you receive all the events related to the
- project."""
-
- queue = asyncio.Queue()
- self._listeners.add(queue)
- return queue
-
- def stop_listen_queue(self, queue):
- """Stop sending notification to this clients"""
-
- self._listeners.remove(queue)
-
- @property
- def listeners(self):
- """
- List of current clients listening for event in this projects
- """
- return self._listeners
+ NotificationManager.instance().emit(action, event, project_id=self.id)
@asyncio.coroutine
def list_files(self):
diff --git a/gns3server/templates/hypervisor.html b/gns3server/templates/hypervisor.html
index 935f8f3d..e706beef 100644
--- a/gns3server/templates/hypervisor.html
+++ b/gns3server/templates/hypervisor.html
@@ -1,7 +1,22 @@
{% extends "layout.html" %}
+
+{% block head %}
+
+{% endblock %}
+
{% block body %}
- Server status
+ Hypervisor status
The purpose of this page is to help for GNS3 debug. This can be dropped
in futur GNS3 versions.
@@ -39,4 +54,9 @@ in futur GNS3 versions.
{{port}}
{% endfor %}
+
+Notifications
+
+
{% endblock %}
+
diff --git a/gns3server/templates/layout.html b/gns3server/templates/layout.html
index 2873ffe4..cf951ade 100644
--- a/gns3server/templates/layout.html
+++ b/gns3server/templates/layout.html
@@ -1,9 +1,7 @@
-
+{% block head %}{% endblock %}
GNS3 Server
diff --git a/gns3server/templates/upload.html b/gns3server/templates/upload.html
index ae9fc033..57191ca2 100644
--- a/gns3server/templates/upload.html
+++ b/gns3server/templates/upload.html
@@ -1,5 +1,7 @@
{% extends "layout.html" %}
-{% block script %}
+
+{% block head %}
+
{% endblock %}
+
{% block body %}
Select & Upload an image for GNS3