diff --git a/monitor-agent/core/command.py b/monitor-agent/core/command.py index d99d8d8..59ee4dd 100644 --- a/monitor-agent/core/command.py +++ b/monitor-agent/core/command.py @@ -1,5 +1,4 @@ import subprocess -import psutil import typing import time import os diff --git a/monitor-agent/core/models/metricModel.py b/monitor-agent/core/models/metricModel.py index 195f5db..39d9265 100644 --- a/monitor-agent/core/models/metricModel.py +++ b/monitor-agent/core/models/metricModel.py @@ -2,7 +2,7 @@ import platform import time import datetime -import asyncio +import os class Status: @@ -31,46 +31,20 @@ class MetricDynamic: def __init__(self): self.cpu_freq = psutil.cpu_freq()._asdict() self.cpu_percent = psutil.cpu_percent() - self.ram = _ram_data() - self.network_current_in, self.network_current_out = asyncio.run( - _cur_network_traffic() - ) + self.ram = psutil.virtual_memory()._asdict() + self.network_current_in, self.network_current_out = _cur_network_traffic() self.network_total_in, self.network_total_out = _total_network_traffic() - # self.connections = {} - # for index, connection in enumerate(psutil.net_connections()): - # self.connections[index] = connection try: self.battery = psutil.sensors_battery()._asdict() except AttributeError: # If no battery is installed or metrics can’t be determined None is returned. self.battery = None self.users = _user_list() - # self.process = {p.pid: p.info for p in psutil.process_iter(['name', 'username'])} + self.processes = _process(self.ram["total"], self.cpu_percent) self.disk_percent = _disk_percent() self.uptime = _uptime() -def _get_size(bytes: int, suffix="B"): - """Scale bytes to its proper format. - e.g: - 1253656 => '1.20MB' - 1253656678 => '1.17GB' - - Args: - bytes (int): Numeric size to format - suffix (str): Format of introduced size: 'B', 'K', 'M', 'G', 'T', 'P' - - Returns: - string: Formatted size - - """ - factor = 1024 - for unit in ["", "K", "M", "G", "T", "P"]: - if bytes < factor: - return f"{bytes:.2f}{unit}{suffix}" - bytes /= factor - - def _format_timestamp(date: float): """_summary_ @@ -92,7 +66,7 @@ def _uptime(): return str(datetime.timedelta(seconds=(time.time() - psutil.boot_time()))) -async def _cur_network_traffic(interval=1): +def _cur_network_traffic(interval=1): """_summary_ Args: @@ -105,7 +79,7 @@ async def _cur_network_traffic(interval=1): net1_out = psutil.net_io_counters().bytes_sent net1_in = psutil.net_io_counters().bytes_recv - await asyncio.sleep(interval) + time.sleep(interval) # Get new net in/out net2_out = psutil.net_io_counters().bytes_sent @@ -115,20 +89,35 @@ async def _cur_network_traffic(interval=1): current_in = 0 if net1_in > net2_in else net2_in - net1_in current_out = 0 if net1_out > net2_out else net2_out - net1_out - # Final Variables - # Traffic in Megabytes - network_current_in = _get_size(round(current_in, 2)) - network_current_out = _get_size(round(current_out, 2)) - - return network_current_in, network_current_out + return current_in, current_out + + +def _process(ram: int, pc_cpu_percent): + process = {} + threshold = 10 + for p in psutil.process_iter(["name", "username"]): + with p.oneshot(): + cpu_percent = p.cpu_percent() + ram_percent = round((p.memory_info().vms / ram) * 100, 2) + if ( + cpu_percent > threshold and not cpu_percent > pc_cpu_percent + ) or ram_percent > threshold: + process[p.pid] = { + "name": p.name(), + "cpu_percent": cpu_percent, + "ram_percent": ram_percent, + "username": p.username(), + "ppid": p.ppid(), + # Requires elevated permissions + # "path": p.exe() + } + return process def _total_network_traffic(): # get IO statistics since boot net_io = psutil.net_io_counters() - network_total_out = _get_size(net_io.bytes_sent) - network_total_in = _get_size(net_io.bytes_recv) - return network_total_in, network_total_out + return net_io.bytes_recv, net_io.bytes_sent def _boot_date(): @@ -136,14 +125,6 @@ def _boot_date(): return _format_timestamp(boot_time_timestamp) -def _ram_data(): - ram = psutil.virtual_memory()._asdict() - for key, value in ram.items(): - if key != "percent": - ram[key] = _get_size(value) - return ram - - def _ip_addresses(): addresses = {} for key, value in psutil.net_if_addrs().items(): @@ -171,15 +152,6 @@ def _disk_list(): try: disk_location = disk.device disk_list[disk_location] = psutil.disk_usage(disk_location)._asdict() - disk_list[disk_location]["free"] = _get_size( - disk_list[disk_location]["free"] - ) - disk_list[disk_location]["total"] = _get_size( - disk_list[disk_location]["total"] - ) - disk_list[disk_location]["used"] = _get_size( - disk_list[disk_location]["used"] - ) except PermissionError as msg: continue return disk_list diff --git a/monitor-agent/main.py b/monitor-agent/main.py index 3e745be..0fd2b1a 100644 --- a/monitor-agent/main.py +++ b/monitor-agent/main.py @@ -12,6 +12,7 @@ endpoints = { "root": "/", "command": "/command", + "metrics_endpoint": "/metrics", } @@ -21,12 +22,20 @@ async def root(): @api.post(endpoints["command"]) -async def command(command: str, timeout: int = 10): +async def command(command: str, timeout: int): # if token: # blablabla return Command(command, timeout).__dict__ +if settings.metrics_endpoint: + + @api.get(endpoints["metrics_endpoint"]) + async def metrics_endpoint(): + elapsed_time, data = send_metrics_adapter([static, dynamic]) + return {"data": data, "elapsed_time": elapsed_time} + + @api.on_event("startup") @repeat_every(seconds=settings.post_task_interval, logger=logger, wait_first=True) def periodic(): @@ -44,4 +53,20 @@ def start(): host=settings.host, port=settings.port, reload=settings.reload, + workers=settings.workers, + log_level=settings.log_level, + interface="asgi3", + # workers=settings.workers, + debug=settings.debug, + ssl_keyfile=settings.ssl_keyfile, + ssl_keyfile_password=settings.ssl_keyfile_password, + ssl_certfile=settings.ssl_certfile, + ssl_version=settings.ssl_version, + ssl_cert_reqs=settings.ssl_cert_reqs, + ssl_ca_certs=settings.ssl_ca_certs, + ssl_ciphers=settings.ssl_ciphers, + limit_concurrency=settings.limit_concurrency, + limit_max_requests=settings.limit_max_requests, + backlog=settings.backlog, + timeout_keep_alive=settings.timeout_keep_alive, ) diff --git a/monitor-agent/settings.py b/monitor-agent/settings.py index b834025..1239bd9 100644 --- a/monitor-agent/settings.py +++ b/monitor-agent/settings.py @@ -1,14 +1,35 @@ from pydantic import BaseSettings - +# https://www.uvicorn.org/settings/ class Settings(BaseSettings): host: str = "0.0.0.0" port: int = 8000 + workers: int = 4 + reload: bool = True + debug: bool = True + log_level: str = "trace" + + ssl_keyfile: str = None + ssl_keyfile_password: str = None + ssl_certfile: str = None + ssl_version: int = 3 + ssl_cert_reqs: int = None + ssl_ca_certs: str = None + ssl_ciphers: str = "TLSv1" + + limit_concurrency: int = None + limit_max_requests: int = None + backlog: int = 2048 + + timeout_keep_alive: int = 5 + + metrics_endpoint: bool = True metrics_URL: str = "http://httpbin.org/post" - alerts_URL: str = "" post_task_interval: int = 60 - metrics_file: str = "metrics.json" + # metrics_file: str = "metrics.json" + + alerts_URL: str = "" settings = Settings() diff --git a/pyproject.toml b/pyproject.toml index 4fdcfb4..dc11559 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,9 +17,9 @@ python = ">=3.7,<4.0.0" requests = "^2.27.1" psutil = "^5.9.0" fastapi = "^0.74.1" +fastapi-utils = "^0.2.1" uvicorn = "^0.17.4" pydantic = "^1.9.0" -fastapi-utils = "^0.2.1" [tool.poetry.dev-dependencies] pytest = "^7.0.0"