From 8e8f664ae7b2ba63ef7874ba1b1ea9c2c20f7a5f Mon Sep 17 00:00:00 2001 From: dormant-user Date: Tue, 10 Sep 2024 06:51:36 -0500 Subject: [PATCH] Include an option to get CPU load averages Add CPU load avg plugin to the monitoring page UI --- docs/genindex.html | 2 + docs/index.html | 16 +++++++ docs/objects.inv | Bin 1531 -> 1537 bytes docs/searchindex.js | 2 +- pyninja/monitor/routes.py | 16 +++++-- pyninja/monitor/templates/main.html | 72 +++++++++++++++++++++++++++- pyninja/routers.py | 30 ++++++++++++ pyninja/squire.py | 2 + release_notes.rst | 38 ++++----------- 9 files changed, 144 insertions(+), 34 deletions(-) diff --git a/docs/genindex.html b/docs/genindex.html index 5a5bda0..f99c24b 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -205,6 +205,8 @@

G

  • get_all_volumes() (in module pyninja.dockerized)
  • get_container_status() (in module pyninja.dockerized) +
  • +
  • get_cpu_load_avg() (in module pyninja.routers)
  • get_cpu_utilization() (in module pyninja.routers)
  • diff --git a/docs/index.html b/docs/index.html index 9780f1a..a387291 100644 --- a/docs/index.html +++ b/docs/index.html @@ -283,6 +283,22 @@

    Welcome to PyNinja’s documentation! +
    +async pyninja.routers.get_cpu_load_avg(request: Request, apikey: HTTPAuthorizationCredentials = Depends(HTTPBearer))
    +

    Get the number of processes in the system run queue averaged over the last 1, 5, and 15 minutes respectively.

    +

    Args:

    +
    +

    request: Reference to the FastAPI request object. +apikey: API Key to authenticate the request.

    +
    +

    Raises:

    +
    +

    APIResponse: +Raises the HTTPStatus object with a status code and CPU usage as response.

    +
    +
    +
    async pyninja.routers.get_disk_utilization(request: Request, apikey: HTTPAuthorizationCredentials = Depends(HTTPBearer))
    diff --git a/docs/objects.inv b/docs/objects.inv index 67f15a9e3af147b26fa7f3c7bbf6cd28bd1a11f4..bd17fa24fdfabd1a3a18a2fdcc5b211922d45022 100644 GIT binary patch delta 279 zcmV+y0qFkw3xN!4Yl*#G7lE5$JadWBjaT*jUUgnCH5;2CRQ}Yx0YYr(Y*~09VD#h zfhZo{$PeFMX;6r+X@E91)>?7I6ZrVWivk|3dzu+`;d`q|dwo*|nCdXJwB)v}x3ib{ zz=A!dFF1Xcn_E*1XurorZe1wB(p4@NZc&in19W>Jxs&zz8;41P8n2IOo1<2OtzP{42Da|- dSYw~l0gE(QJqPo<%xv=SYuq_+{s)z7tdHnEk%IsL delta 237 zcmV+3WA6T%*^bLlm z3(~n&!GQL=ROHr$5-eTDV(AqH2|hr#2a-Ek8d>_*M1mTxk7=W#lVJrD1$CLl;v
    @@ -182,6 +187,7 @@

    PyNinja - System Monitor

    +

    CPU Usage

    @@ -200,12 +206,14 @@

    Set Refresh Interval (seconds)

    +

    Memory Usage

    Memory: 0%

    + {% if swap %}

    Swap Usage

    @@ -213,12 +221,19 @@

    Swap Usage

    Swap: 0%

    {% endif %} +

    Disk Usage

    Disk: 0%

    + +
    +

    CPU Load Averages

    + +
    +

    Memory Usage

    @@ -249,6 +264,7 @@
    let memoryChartInstance = null; let swapChartInstance = null; let diskChartInstance = null; + let loadChartInstance = null; ws.onmessage = function (event) { const date = new Date(); @@ -259,7 +275,7 @@
    } catch (error) { console.warn('Error parsing JSON data:', error); alert(event.data); - window.location.href = `${window.location.origin}/logout`; + logOut(); return; } // Update CPU usage @@ -301,6 +317,60 @@
    document.getElementById('diskUsageText').innerText = `Disk: ${diskUsage.toFixed(2)}%`; updateProgressBar('diskUsage', diskUsage); + // CPU Load Avg Graph + const loadAverages = data.load_averages; + if (loadChartInstance) { + loadChartInstance.data.datasets[0].data = [loadAverages["1m"], loadAverages["5m"], loadAverages["15m"]]; + loadChartInstance.update(); + } else { + const ctx = document.getElementById('loadChart').getContext('2d'); + loadChartInstance = new Chart(ctx, { + type: 'bar', + data: { + labels: ['1 minute', '5 minutes', '15 minutes'], + datasets: [{ + label: 'Load Average', + data: [loadAverages["1m"], loadAverages["5m"], loadAverages["15m"]], + backgroundColor: [ + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)' + ], + borderColor: [ + 'rgba(75, 192, 192, 1)', + 'rgba(153, 102, 255, 1)', + 'rgba(255, 159, 64, 1)' + ], + borderWidth: 1 + }] + }, + options: { + plugins: { + // Hide the legend + legend: { + display: false + } + }, + scales: { + y: { + beginAtZero: true, + title: { + display: true, + text: 'Number of Processes' + }, + ticks: { + // Set integer step size + stepSize: 1, + callback: function (value) { + return Number.isInteger(value) ? value : ''; + } + } + } + } + } + }); + } + // Memory Chart document.getElementById("memoryTotal").innerText = `Total: ${formatBytes(memoryInfo.total)}`; if (memoryChartInstance) { diff --git a/pyninja/routers.py b/pyninja/routers.py index ce51c77..c8756e2 100644 --- a/pyninja/routers.py +++ b/pyninja/routers.py @@ -101,6 +101,30 @@ async def get_memory_utilization( ) +async def get_cpu_load_avg( + request: Request, + apikey: HTTPAuthorizationCredentials = Depends(BEARER_AUTH), +): + """**Get the number of processes in the system run queue averaged over the last 1, 5, and 15 minutes respectively.** + + **Args:** + + request: Reference to the FastAPI request object. + apikey: API Key to authenticate the request. + + **Raises:** + + APIResponse: + Raises the HTTPStatus object with a status code and CPU usage as response. + """ + await auth.level_1(request, apikey) + m1, m5, m15 = psutil.getloadavg() or (None, None, None) + raise exceptions.APIResponse( + status_code=HTTPStatus.OK.real, + detail={"1m": m1, "5m": m5, "15m": m15}, + ) + + async def get_disk_utilization( request: Request, apikey: HTTPAuthorizationCredentials = Depends(BEARER_AUTH), @@ -402,6 +426,12 @@ def get_all_routes(dependencies: List[Depends]) -> List[APIRoute]: methods=["GET"], dependencies=dependencies, ), + APIRoute( + path="/get-cpu-load", + endpoint=get_cpu_load_avg, + methods=["GET"], + dependencies=dependencies, + ), APIRoute( path="/get-processor", endpoint=get_processor_name, diff --git a/pyninja/squire.py b/pyninja/squire.py index c153f63..fb8b2e0 100644 --- a/pyninja/squire.py +++ b/pyninja/squire.py @@ -81,11 +81,13 @@ def system_resources(cpu_interval: int) -> Dict[str, dict]: Dict[str, dict]: Returns a nested dictionary. """ + m1, m5, m15 = os.getloadavg() or (None, None, None) return dict( cpu_usage=psutil.cpu_percent(interval=cpu_interval, percpu=True), memory_info=psutil.virtual_memory()._asdict(), swap_info=psutil.swap_memory()._asdict(), disk_info=shutil.disk_usage("/")._asdict(), + load_averages={"1m": m1, "5m": m5, "15m": m15}, ) diff --git a/release_notes.rst b/release_notes.rst index ec570e3..42be588 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -3,58 +3,40 @@ Release Notes v0.0.7 (09/09/2024) ------------------- -- Includes a new feature to monitor disk utilization and get process name -- Bug fix on uncaught errors during server shutdown -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.6...v0.0.7 +- Release `v0.0.7` v0.0.6 (09/09/2024) ------------------- -- Includes an option to limit maximum number of WebSocket sessions -- Includes a logout functionality for the monitoring page -- Uses bearer auth for the monitoring page -- Redefines progress bars with newer color schemes -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.5...v0.0.6 +- Release `v0.0.6` v0.0.6a (09/07/2024) -------------------- -- Includes an option to limit max number of concurrent sessions for monitoring page -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.5...v0.0.6a +- Release `v0.0.6a` v0.0.5 (09/07/2024) ------------------- -- Packs an entirely new UI and authentication mechanism for monitoring tool -- Includes speed, stability and security improvements for monitoring feature -- Adds night mode option for monitoring UI -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.4...v0.0.5 +- Release `v0.0.5` v0.0.4 (09/06/2024) ------------------- -- Includes an option to monitor system resources via `WebSockets` -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.3...v0.0.4 +- Include an option to monitor system resources via websockets v0.0.3 (08/16/2024) ------------------- -- Allows env vars to be sourced from both ``env_file`` and ``kwargs`` -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.2...v0.0.3 +- Release `v0.0.3` v0.0.2 (08/16/2024) ------------------- -- Includes added support for custom log configuration -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.1...v0.0.2 +- Release `v0.0.2` v0.0.1 (08/11/2024) ------------------- -- Includes a process monitor and remote command execution functionality -- Security improvements including brute force protection and rate limiting -- Accepts ``JSON`` and ``YAML`` files for env config -- Supports custom worker count for ``uvicorn`` server -- Allows custom logging using ``logging.ini`` -- Includes an option to set the ``apikey`` via commandline -- **Full Changelog**: https://github.com/thevickypedia/PyNinja/compare/v0.0.0...v0.0.1 +- Release `v0.0.1` v0.0.0 (08/11/2024) ------------------- -- Release first stable version +- Implement concurrency for validating process health +- Update logger names across the module and README.md 0.0.0-a (08/10/2024) --------------------