Compare commits
8 Commits
869763dde3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c5e003f08 | ||
| 97ea4abac8 | |||
| edfd2cf043 | |||
|
07b9ba6fdf
|
|||
|
14e69fc406
|
|||
|
d6415712d9
|
|||
|
9360fe9f3c
|
|||
|
5235cef103
|
19
CHANGELOG.md
19
CHANGELOG.md
@@ -2,6 +2,25 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.5.0] - 2026-03-08
|
||||
|
||||
### Features
|
||||
|
||||
- [`d641571`](https://gitea.pufereq.pl/judas/judas_client/commit/d6415712d97c27a1716a836e0cde440b486d9132) **connector.py**: send initial telemetry after HELLO
|
||||
- [`9360fe9`](https://gitea.pufereq.pl/judas/judas_client/commit/9360fe9f3c600cd7c610e8fd6e2d80f91a8c341f) **connector.py**: add `self.client` property
|
||||
- [`5235cef`](https://gitea.pufereq.pl/judas/judas_client/commit/5235cef103a4b12f84fbc9def6bb68660041d8e1) **client.py**: add initial telemetry gathering
|
||||
|
||||
### Refactor
|
||||
|
||||
- [`07b9ba6`](https://gitea.pufereq.pl/judas/judas_client/commit/07b9ba6fdf7194a06520357327048a33b309ed5b) **client.py**: improve CPU model retrivement on Windows
|
||||
|
||||
### Build
|
||||
|
||||
- [`14e69fc`](https://gitea.pufereq.pl/judas/judas_client/commit/14e69fc40606961d3e3cabbda9119211c81fc972) **uv.lock**: update judas_protocol to 0.9.1
|
||||
- [`869763d`](https://gitea.pufereq.pl/judas/judas_client/commit/869763dde35d18522d4e84bb9436b1d5064364e8) **uv.lock**: add depedency on psutil
|
||||
- [`dd5fccf`](https://gitea.pufereq.pl/judas/judas_client/commit/dd5fccf8ff7ab214cec277ce6f8ccbc39acc85bb) **pyproject.toml**: add depedency on psutil
|
||||
- [`a4a9051`](https://gitea.pufereq.pl/judas/judas_client/commit/a4a9051130a304b6e2d6ed038c62f812dde2c6a4) **uv.lock**: update judas_protocol to 0.9.0
|
||||
|
||||
## [0.4.4] - 2026-03-05
|
||||
|
||||
### Refactor
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
||||
|
||||
[project]
|
||||
name = "judas_client"
|
||||
version = "0.4.4"
|
||||
version = "0.5.0"
|
||||
description = "A client for judas, a remote PC fleet management system."
|
||||
readme = "README.md"
|
||||
authors = []
|
||||
|
||||
@@ -5,8 +5,12 @@ from __future__ import annotations
|
||||
|
||||
import logging as lg
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
import psutil
|
||||
from judas_protocol import Message
|
||||
|
||||
from judas_client.connector import Connector
|
||||
@@ -41,12 +45,17 @@ class Client:
|
||||
self.logger.debug(f"ID: {self.id}")
|
||||
|
||||
self.connector: Connector = Connector(
|
||||
client=self,
|
||||
mac_address=self.id,
|
||||
host=self.server_host,
|
||||
port=self.server_port,
|
||||
on_message=self.handle_message,
|
||||
)
|
||||
|
||||
self.initial_telemetry: dict[str, Any] = (
|
||||
self._gather_initial_telemetry()
|
||||
)
|
||||
|
||||
def _get_mac_address(self) -> str:
|
||||
"""Get the MAC address of the client.
|
||||
|
||||
@@ -61,6 +70,144 @@ class Client:
|
||||
)
|
||||
return mac_address
|
||||
|
||||
def _get_cpu_model(self) -> str:
|
||||
"""Get the CPU model name.
|
||||
|
||||
Returns:
|
||||
str: The CPU model name, or "Unknown" if it cannot be determined.
|
||||
"""
|
||||
|
||||
system = platform.system()
|
||||
|
||||
if system == "Windows":
|
||||
try:
|
||||
import winreg
|
||||
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"HARDWARE\DESCRIPTION\System\CentralProcessor\0",
|
||||
)
|
||||
model, _ = winreg.QueryValueEx(key, "ProcessorNameString")
|
||||
winreg.CloseKey(key)
|
||||
return model.strip()
|
||||
except Exception:
|
||||
return platform.processor()
|
||||
|
||||
elif system == "Linux":
|
||||
try:
|
||||
with open("/proc/cpuinfo", "r") as f:
|
||||
for line in f:
|
||||
if "model name" in line:
|
||||
return line.split(":")[1].strip()
|
||||
except Exception:
|
||||
return platform.processor()
|
||||
|
||||
elif system == "Darwin":
|
||||
try:
|
||||
return (
|
||||
subprocess.check_output(
|
||||
["sysctl", "-n", "machdep.cpu.brand_string"]
|
||||
)
|
||||
.decode()
|
||||
.strip()
|
||||
)
|
||||
except Exception:
|
||||
return platform.processor()
|
||||
|
||||
return platform.processor()
|
||||
|
||||
def _gather_initial_telemetry(self) -> dict[str, Any]:
|
||||
"""Gather initial telemetry data."""
|
||||
self.logger.debug("Gathering initial telemetry data...")
|
||||
inventory: dict[str, Any] = {}
|
||||
|
||||
# platform info
|
||||
inventory["platform"] = {
|
||||
"system": platform.system(),
|
||||
"node": platform.node(),
|
||||
"release": platform.release(),
|
||||
"version": platform.version(),
|
||||
"architecture": platform.architecture()[0],
|
||||
}
|
||||
|
||||
# CPU information
|
||||
cpu_frequency = psutil.cpu_freq()
|
||||
inventory["cpu"] = {
|
||||
"model": self._get_cpu_model(),
|
||||
"physical_cores": psutil.cpu_count(logical=False),
|
||||
"threads": psutil.cpu_count(logical=True),
|
||||
"max_frequency_mhz": round(cpu_frequency.max, 2)
|
||||
if cpu_frequency
|
||||
else "Unknown",
|
||||
"min_frequency_mhz": round(cpu_frequency.min, 2)
|
||||
if cpu_frequency
|
||||
else "Unknown",
|
||||
}
|
||||
|
||||
# Memory information
|
||||
virtual_memory = psutil.virtual_memory()
|
||||
swap_memory = psutil.swap_memory()
|
||||
inventory["memory"] = {
|
||||
"total_ram": round(virtual_memory.total / (1024**2), 2),
|
||||
"total_swap": round(swap_memory.total / (1024**2), 2),
|
||||
}
|
||||
|
||||
# Disk information
|
||||
disk_partitions = psutil.disk_partitions()
|
||||
inventory["disks"] = []
|
||||
for partition in disk_partitions:
|
||||
try:
|
||||
usage = psutil.disk_usage(partition.mountpoint)
|
||||
inventory["disks"].append(
|
||||
{
|
||||
"device": partition.device,
|
||||
"mountpoint": partition.mountpoint,
|
||||
"filesystem": partition.fstype,
|
||||
"total_size_gb": round(usage.total / (1024**3), 2),
|
||||
}
|
||||
)
|
||||
except (PermissionError, OSError) as e:
|
||||
self.logger.warning(
|
||||
f"Permission denied for disk partition: {partition.device}\n{e}"
|
||||
)
|
||||
continue
|
||||
|
||||
# Network information
|
||||
network_interfaces = psutil.net_if_addrs()
|
||||
inventory["network"] = []
|
||||
for interface_name, addresses in network_interfaces.items():
|
||||
interface_data = {
|
||||
"interface": interface_name,
|
||||
"ip_addresses": [],
|
||||
"mac_address": None,
|
||||
}
|
||||
for address in addresses:
|
||||
if address.family.name in ["AF_LINK", "AF_PACKET"]:
|
||||
interface_data["mac_address"] = address.address
|
||||
elif address.family.name == "AF_INET":
|
||||
interface_data["ip_addresses"].append(address.address)
|
||||
inventory["network"].append(interface_data)
|
||||
|
||||
# User information
|
||||
inventory["users"] = []
|
||||
for user in psutil.users():
|
||||
inventory["users"].append(
|
||||
{
|
||||
"name": user.name,
|
||||
"terminal": user.terminal,
|
||||
"host": user.host,
|
||||
"started": user.started,
|
||||
}
|
||||
)
|
||||
|
||||
# Python environment information
|
||||
inventory["python"] = {
|
||||
"version": platform.python_version(),
|
||||
"implementation": platform.python_implementation(),
|
||||
}
|
||||
|
||||
return inventory
|
||||
|
||||
def handle_message(self, message: Message) -> None:
|
||||
"""Handle incoming messages.
|
||||
|
||||
|
||||
@@ -5,16 +5,20 @@ import logging as lg
|
||||
import selectors
|
||||
import socket
|
||||
import time
|
||||
from typing import Callable
|
||||
from typing import Callable, TYPE_CHECKING
|
||||
|
||||
from judas_protocol import Category, ControlAction, Message
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from judas_client.client import Client
|
||||
|
||||
|
||||
class Connector:
|
||||
"""Connector class for managing TCP connection and message exchange."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: Client,
|
||||
mac_address: str,
|
||||
host: str,
|
||||
port: int,
|
||||
@@ -34,6 +38,8 @@ class Connector:
|
||||
)
|
||||
self.logger.debug("Initializing Connector...")
|
||||
|
||||
self.client: Client = client
|
||||
|
||||
self.host: str = host
|
||||
self.port: int = port
|
||||
|
||||
@@ -110,6 +116,14 @@ class Connector:
|
||||
hello_message: Message = Message.Control.hello(self.mac_address)
|
||||
self.send(hello_message)
|
||||
|
||||
def send_initial_telemetry(self) -> None:
|
||||
"""Send initial telemetry data to the server."""
|
||||
self.logger.debug("[*] Sending initial telemetry...")
|
||||
telemetry_message: Message = Message.Telemetry.initial(
|
||||
self.client.initial_telemetry
|
||||
)
|
||||
self.send(telemetry_message)
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the connection and clean up resources."""
|
||||
self.logger.debug("[*] Closing connection...")
|
||||
@@ -171,6 +185,7 @@ class Connector:
|
||||
|
||||
self.logger.debug("[*] Connected, sending HELLO...")
|
||||
self.send_hello()
|
||||
self.send_initial_telemetry()
|
||||
|
||||
def run(self) -> None:
|
||||
"""Run the main event loop."""
|
||||
|
||||
6
uv.lock
generated
6
uv.lock
generated
@@ -275,7 +275,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "judas-client"
|
||||
version = "0.4.4"
|
||||
version = "0.5.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "judas-protocol" },
|
||||
@@ -322,8 +322,8 @@ test = [
|
||||
|
||||
[[package]]
|
||||
name = "judas-protocol"
|
||||
version = "0.9.0"
|
||||
source = { git = "https://gitea.pufereq.pl/judas/judas_protocol.git#3d5a1e95daa4cd99b51abdcaca9967fa8f921ec1" }
|
||||
version = "0.9.1"
|
||||
source = { git = "https://gitea.pufereq.pl/judas/judas_protocol.git#085c34f232f95313d66db48a7d17bc25c92a35ae" }
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
|
||||
Reference in New Issue
Block a user