Notification feed for the hypervisor

This commit is contained in:
Julien Duponchelle committed 2016-03-17 15:15:30 +01:00
1 parent d81fc25b27
commit b55969d381
15 files changed
+425 -144

No files matched your search

@@ -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":
@@ -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 <http://www.gnu.org/licenses/>.
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
@@ -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 <http://www.gnu.org/licenses/>.
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
+2 -25
View File
@@ -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):
+21 -1
View File
@@ -1,7 +1,22 @@
{% extends "layout.html" %}
{% block head %}
<script>
var socket = new WebSocket("ws://" + location.host + "/v2/hypervisor/notifications/ws");
socket.onopen = function (event) {
document.getElementById("notifications").innerText = "Connected";
};
socket.onmessage = function (event) {
document.getElementById("notifications").innerText = event.data + "\n" + document.getElementById("notifications").innerText;
};
</script>
{% endblock %}
{% block body %}
<h1>
Server status
Hypervisor status
</h1>
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.
<li>{{port}}</li>
{% endfor %}
</ul>
<h2>Notifications</h2>
<div id="notifications">
</div>
{% endblock %}
+1 -3
View File
@@ -1,9 +1,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script>
{% block script %}{% endblock %}
</script>
{% block head %}{% endblock %}
<title>GNS3 Server</title>
</head>
<body>
+5 -1
View File
@@ -1,5 +1,7 @@
{% extends "layout.html" %}
{% block script %}
{% block head %}
<script>
function onSubmit() {
if (document.getElementById("uploadInput").files == undefined) {
//OLD browser
@@ -15,7 +17,9 @@ function onSubmit() {
}
return true;
}
</script>
{% endblock %}
{% block body %}
<h1>Select & Upload an image for GNS3</h1>
<form enctype="multipart/form-data" action="/upload" method="post" onSubmit="return onSubmit()">