From 3a09bd43dc1514f285f6fd69317756ccd4068611 Mon Sep 17 00:00:00 2001 From: Bernhard Ehlers Date: Tue, 13 Feb 2018 11:31:04 +0100 Subject: [PATCH] Implement a minimum interval between psutil calls. Fixes #2262 --- .../handlers/api/compute/project_handler.py | 8 +-- gns3server/notification_queue.py | 17 ++----- gns3server/utils/ping_stats.py | 50 +++++++++++++++++++ 3 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 gns3server/utils/ping_stats.py diff --git a/gns3server/handlers/api/compute/project_handler.py b/gns3server/handlers/api/compute/project_handler.py index 73662ca7..a7d8f760 100644 --- a/gns3server/handlers/api/compute/project_handler.py +++ b/gns3server/handlers/api/compute/project_handler.py @@ -19,12 +19,12 @@ import aiohttp import asyncio import json import os -import psutil import tempfile from gns3server.web.route import Route from gns3server.compute.project_manager import ProjectManager from gns3server.compute import MODULES +from gns3server.utils.ping_stats import PingStats from gns3server.schemas.project import ( PROJECT_OBJECT_SCHEMA, @@ -186,11 +186,7 @@ class ProjectHandler: :returns: hash """ - stats = {} - # Non blocking call in order to get cpu usage. First call will return 0 - stats["cpu_usage_percent"] = psutil.cpu_percent(interval=None) - stats["memory_usage_percent"] = psutil.virtual_memory().percent - return {"action": "ping", "event": stats} + return {"action": "ping", "event": PingStats.get()} @Route.get( r"/projects/{project_id}/files", diff --git a/gns3server/notification_queue.py b/gns3server/notification_queue.py index 88cbaec3..2412d5cb 100644 --- a/gns3server/notification_queue.py +++ b/gns3server/notification_queue.py @@ -17,7 +17,8 @@ import asyncio import json -import psutil + +from gns3server.utils.ping_stats import PingStats class NotificationQueue(asyncio.Queue): @@ -38,24 +39,14 @@ class NotificationQueue(asyncio.Queue): # At first get we return a ping so the client immediately receives data if self._first: self._first = False - return ("ping", self._getPing(), {}) + return ("ping", PingStats.get(), {}) try: (action, msg, kwargs) = yield from asyncio.wait_for(super().get(), timeout) except asyncio.futures.TimeoutError: - return ("ping", self._getPing(), {}) + return ("ping", PingStats.get(), {}) 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): """ diff --git a/gns3server/utils/ping_stats.py b/gns3server/utils/ping_stats.py new file mode 100644 index 00000000..3252f9c2 --- /dev/null +++ b/gns3server/utils/ping_stats.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2018 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 psutil +import time + + +class PingStats: + """ + Ping messages are regularly sent to the client to keep the connection open. + We send with it some information about server load. + """ + + _last_measurement = 0.0 # time of last measurement + _last_cpu_percent = 0.0 # last cpu_percent + _last_mem_percent = 0.0 # last virtual_memory().percent + + @classmethod + def get(cls): + """ + Get ping statistics + + :returns: hash + """ + stats = {} + cur_time = time.time() + # minimum interval for getting CPU and memory statistics + if cur_time < cls._last_measurement or \ + cur_time > cls._last_measurement + 1.9: + cls._last_measurement = cur_time + # Non blocking call to get cpu usage. First call will return 0 + cls._last_cpu_percent = psutil.cpu_percent(interval=None) + cls._last_mem_percent = psutil.virtual_memory().percent + stats["cpu_usage_percent"] = cls._last_cpu_percent + stats["memory_usage_percent"] = cls._last_mem_percent + return stats