1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 00:38:10 +00:00

Add total RAM, CPUs and disk size to servers summary as well as disk usage in percent. Fixes https://github.com/GNS3/gns3-server/issues/1532

This commit is contained in:
grossmj 2020-07-19 14:16:07 +09:30
parent 00a6765405
commit 3dc4ea46d6
11 changed files with 69 additions and 9 deletions

View File

@ -80,9 +80,13 @@ class Compute:
self._set_auth(user, password) self._set_auth(user, password)
self._cpu_usage_percent = None self._cpu_usage_percent = None
self._memory_usage_percent = None self._memory_usage_percent = None
self._disk_usage_percent = None
self._last_error = None self._last_error = None
self._capabilities = { self._capabilities = {
"version": None, "version": None,
"cpus": None,
"memory": None,
"disk_size": None,
"node_types": [] "node_types": []
} }
self.name = name self.name = name
@ -270,6 +274,10 @@ class Compute:
def memory_usage_percent(self): def memory_usage_percent(self):
return self._memory_usage_percent return self._memory_usage_percent
@property
def disk_usage_percent(self):
return self._disk_usage_percent
def __json__(self, topology_dump=False): def __json__(self, topology_dump=False):
""" """
:param topology_dump: Filter to keep only properties require for saving on disk :param topology_dump: Filter to keep only properties require for saving on disk
@ -292,6 +300,7 @@ class Compute:
"connected": self._connected, "connected": self._connected,
"cpu_usage_percent": self._cpu_usage_percent, "cpu_usage_percent": self._cpu_usage_percent,
"memory_usage_percent": self._memory_usage_percent, "memory_usage_percent": self._memory_usage_percent,
"disk_usage_percent": self._disk_usage_percent,
"capabilities": self._capabilities, "capabilities": self._capabilities,
"last_error": self._last_error "last_error": self._last_error
} }
@ -437,6 +446,7 @@ class Compute:
if action == "ping": if action == "ping":
self._cpu_usage_percent = event["cpu_usage_percent"] self._cpu_usage_percent = event["cpu_usage_percent"]
self._memory_usage_percent = event["memory_usage_percent"] self._memory_usage_percent = event["memory_usage_percent"]
self._disk_usage_percent = event["disk_usage_percent"]
#FIXME: slow down number of compute events #FIXME: slow down number of compute events
self._controller.notification.controller_emit("compute.updated", self.__json__()) self._controller.notification.controller_emit("compute.updated", self.__json__())
else: else:
@ -461,6 +471,7 @@ class Compute:
self._cpu_usage_percent = None self._cpu_usage_percent = None
self._memory_usage_percent = None self._memory_usage_percent = None
self._disk_usage_percent = None
self._controller.notification.controller_emit("compute.updated", self.__json__()) self._controller.notification.controller_emit("compute.updated", self.__json__())
def _getUrl(self, path): def _getUrl(self, path):

View File

@ -16,11 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys import sys
import psutil
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.schemas.capabilities import CAPABILITIES_SCHEMA from gns3server.schemas.capabilities import CAPABILITIES_SCHEMA
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.utils.path import get_default_project_directory
class CapabilitiesHandler: class CapabilitiesHandler:
@ -38,5 +40,8 @@ class CapabilitiesHandler:
response.json({ response.json({
"version": __version__, "version": __version__,
"platform": sys.platform, "platform": sys.platform,
"cpus": psutil.cpu_count(logical=True),
"memory": psutil.virtual_memory().total,
"disk_size": psutil.disk_usage(get_default_project_directory()).total,
"node_types": node_types "node_types": node_types
}) })

View File

@ -20,12 +20,12 @@ import asyncio
import json import json
import os import os
import psutil import psutil
import tempfile
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.utils.cpu_percent import CpuPercent from gns3server.utils.cpu_percent import CpuPercent
from gns3server.utils.path import get_default_project_directory
from gns3server.schemas.project import ( from gns3server.schemas.project import (
PROJECT_OBJECT_SCHEMA, PROJECT_OBJECT_SCHEMA,
@ -211,6 +211,7 @@ class ProjectHandler:
# Non blocking call in order to get cpu usage. First call will return 0 # Non blocking call in order to get cpu usage. First call will return 0
stats["cpu_usage_percent"] = CpuPercent.get(interval=None) stats["cpu_usage_percent"] = CpuPercent.get(interval=None)
stats["memory_usage_percent"] = psutil.virtual_memory().percent stats["memory_usage_percent"] = psutil.virtual_memory().percent
stats["disk_usage_percent"] = psutil.disk_usage(get_default_project_directory()).percent
return {"action": "ping", "event": stats} return {"action": "ping", "event": stats}
@Route.get( @Route.get(

View File

@ -25,6 +25,7 @@ from gns3server.schemas.server_statistics import SERVER_STATISTICS_SCHEMA
from gns3server.compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
from gns3server.utils.cpu_percent import CpuPercent from gns3server.utils.cpu_percent import CpuPercent
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.utils.path import get_default_project_directory
from aiohttp.web import HTTPConflict from aiohttp.web import HTTPConflict
@ -61,7 +62,7 @@ class ServerHandler:
load_average_percent = [int(x / psutil.cpu_count() * 100) for x in psutil.getloadavg()] load_average_percent = [int(x / psutil.cpu_count() * 100) for x in psutil.getloadavg()]
memory_percent = int(psutil.virtual_memory().percent) memory_percent = int(psutil.virtual_memory().percent)
swap_percent = int(psutil.swap_memory().percent) swap_percent = int(psutil.swap_memory().percent)
disk_usage_percent = int(psutil.disk_usage('/').percent) disk_usage_percent = int(psutil.disk_usage(get_default_project_directory()).percent)
except psutil.Error as e: except psutil.Error as e:
raise HTTPConflict(text="Psutil error detected: {}".format(e)) raise HTTPConflict(text="Psutil error detected: {}".format(e))
response.json({"memory_total": memory_total, response.json({"memory_total": memory_total,

View File

@ -20,6 +20,7 @@ import json
import psutil import psutil
from gns3server.utils.cpu_percent import CpuPercent from gns3server.utils.cpu_percent import CpuPercent
from gns3server.utils.path import get_default_project_directory
class NotificationQueue(asyncio.Queue): class NotificationQueue(asyncio.Queue):
@ -55,6 +56,7 @@ class NotificationQueue(asyncio.Queue):
# Non blocking call in order to get cpu usage. First call will return 0 # Non blocking call in order to get cpu usage. First call will return 0
msg["cpu_usage_percent"] = CpuPercent.get(interval=None) msg["cpu_usage_percent"] = CpuPercent.get(interval=None)
msg["memory_usage_percent"] = psutil.virtual_memory().percent msg["memory_usage_percent"] = psutil.virtual_memory().percent
msg["disk_usage_percent"] = psutil.disk_usage(get_default_project_directory()).percent
return msg return msg
async def get_json(self, timeout): async def get_json(self, timeout):

View File

@ -36,7 +36,19 @@ CAPABILITIES_SCHEMA = {
"platform": { "platform": {
"type": "string", "type": "string",
"description": "Platform where the compute is running" "description": "Platform where the compute is running"
} },
"cpus": {
"description": "Number of CPUs on this compute. Read only",
"type": ["integer", "null"],
},
"memory": {
"description": "Amount of memory on this compute. Read only",
"type": ["integer", "null"],
},
"disk_size": {
"description": "Disk size on this compute. Read only",
"type": ["integer", "null"],
},
}, },
"additionalProperties": False "additionalProperties": False
} }

View File

@ -104,6 +104,12 @@ COMPUTE_OBJECT_SCHEMA = {
"maximum": 100, "maximum": 100,
"minimum": 0 "minimum": 0
}, },
"disk_usage_percent": {
"description": "Disk usage of the compute. Read only",
"type": ["number", "null"],
"maximum": 100,
"minimum": 0
},
"last_error": { "last_error": {
"description": "Last error on the compute", "description": "Last error on the compute",
"type": ["string", "null"] "type": ["string", "null"]

View File

@ -292,10 +292,14 @@ async def test_json(compute):
"user": "test", "user": "test",
"cpu_usage_percent": None, "cpu_usage_percent": None,
"memory_usage_percent": None, "memory_usage_percent": None,
"disk_usage_percent": None,
"connected": True, "connected": True,
"last_error": None, "last_error": None,
"capabilities": { "capabilities": {
"version": None, "version": None,
"cpus": None,
"memory": None,
"disk_size": None,
"node_types": [] "node_types": []
} }
} }

View File

@ -18,8 +18,10 @@
import sys import sys
import pytest import pytest
import psutil
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.utils.path import get_default_project_directory
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@ -27,7 +29,13 @@ async def test_get(compute_api, windows_platform):
response = await compute_api.get('/capabilities') response = await compute_api.get('/capabilities')
assert response.status == 200 assert response.status == 200
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform} assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'],
'version': __version__,
'platform': sys.platform,
'cpus': psutil.cpu_count(logical=True),
'memory': psutil.virtual_memory().total,
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
}
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@ -35,4 +43,10 @@ async def test_get_on_gns3vm(compute_api, on_gns3vm):
response = await compute_api.get('/capabilities') response = await compute_api.get('/capabilities')
assert response.status == 200 assert response.status == 200
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], 'version': __version__, 'platform': sys.platform} assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'],
'version': __version__,
'platform': sys.platform,
'cpus': psutil.cpu_count(logical=True),
'memory': psutil.virtual_memory().total,
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
}

View File

@ -133,9 +133,13 @@ async def test_compute_list(controller_api):
'name': 'My super server', 'name': 'My super server',
'cpu_usage_percent': None, 'cpu_usage_percent': None,
'memory_usage_percent': None, 'memory_usage_percent': None,
'disk_usage_percent': None,
'last_error': None, 'last_error': None,
'capabilities': { 'capabilities': {
'version': None, 'version': None,
'cpus': None,
'memory': None,
'disk_size': None,
'node_types': [] 'node_types': []
} }
} }

View File

@ -53,7 +53,7 @@ def write_config(tmpdir, settings):
return path return path
def test_get_section_config(tmpdir): def test_get_section_config(loop, tmpdir):
config = load_config(tmpdir, { config = load_config(tmpdir, {
"Server": { "Server": {
@ -63,7 +63,7 @@ def test_get_section_config(tmpdir):
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"} assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
def test_set_section_config(tmpdir): def test_set_section_config(loop, tmpdir):
config = load_config(tmpdir, { config = load_config(tmpdir, {
"Server": { "Server": {
@ -77,7 +77,7 @@ def test_set_section_config(tmpdir):
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"} assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"}
def test_set(tmpdir): def test_set(loop, tmpdir):
config = load_config(tmpdir, { config = load_config(tmpdir, {
"Server": { "Server": {
@ -90,7 +90,7 @@ def test_set(tmpdir):
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
def test_reload(tmpdir): def test_reload(loop, tmpdir):
config = load_config(tmpdir, { config = load_config(tmpdir, {
"Server": { "Server": {