Compare commits

...

4 Commits

Author SHA1 Message Date
wh1te909
1e3ee91cef fix flake 2025-01-24 22:54:23 +00:00
wh1te909
74721b6d61 blacked 2025-01-24 22:43:42 +00:00
wh1te909
2190943faf debugging black 2025-01-24 22:28:15 +00:00
wh1te909
eb80f07b7d monitoring view v2 2025-01-24 22:01:54 +00:00
5 changed files with 117 additions and 5 deletions

View File

@@ -52,7 +52,7 @@ jobs:
- name: Codestyle black
working-directory: api
run: |
black --exclude migrations/ --check tacticalrmm
black --exclude migrations/ --check --diff tacticalrmm
if [ $? -ne 0 ]; then
exit 1
fi

View File

@@ -1,9 +1,11 @@
import json
from functools import wraps
from django.conf import settings
from django.http import HttpResponse
# TODO deprecated
def monitoring_view(function):
def wrap(request, *args, **kwargs):
if request.method != "POST":
@@ -29,3 +31,25 @@ def monitoring_view(function):
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
def monitoring_view_v2(function):
@wraps(function)
def wrap(request, *args, **kwargs):
if request.method != "GET":
return HttpResponse("Invalid request type\n", status=400)
http_token = request.META.get("HTTP_X_MON_TOKEN")
if not http_token:
return HttpResponse("Missing X-Mon-Token header\n", status=401)
mon_token = getattr(settings, "MON_TOKEN", "")
if not mon_token:
return HttpResponse("Missing mon token\n", status=401)
if http_token != mon_token:
return HttpResponse("Not authenticated\n", status=401)
return function(request, *args, **kwargs)
return wrap

View File

@@ -20,7 +20,8 @@ urlpatterns = [
path("urlaction/run/test/", views.RunTestURLAction.as_view()),
path("smstest/", views.TwilioSMSTest.as_view()),
path("clearcache/", views.clear_cache),
path("status/", views.status),
path("status/", views.status), # TODO deprecated
path("v2/status/", views.status_v2),
path("openai/generate/", views.OpenAICodeCompletion.as_view()),
path("webtermperms/", views.webterm_perms),
]

View File

@@ -20,7 +20,7 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from core.decorators import monitoring_view
from core.decorators import monitoring_view, monitoring_view_v2
from core.tasks import sync_mesh_perms_task
from core.utils import (
get_core_settings,
@@ -32,6 +32,7 @@ from core.utils import (
from logs.models import AuditLog
from tacticalrmm.constants import AuditActionType, PAStatus
from tacticalrmm.helpers import get_certs, notify_error
from tacticalrmm.logger import logger
from tacticalrmm.permissions import (
_has_perm_on_agent,
_has_perm_on_client,
@@ -557,6 +558,72 @@ class TwilioSMSTest(APIView):
return Response(msg)
@csrf_exempt
@monitoring_view_v2
def status_v2(request):
from agents.models import Agent
from clients.models import Client, Site
from tacticalrmm.helpers import get_nats_ports
from tacticalrmm.utils import get_celery_queue_len, localhost_port_is_open
disk_usage: int = round(psutil.disk_usage("/").percent)
mem_usage: int = round(psutil.virtual_memory().percent)
cert_file, _ = get_certs()
cert_bytes = Path(cert_file).read_bytes()
cert = x509.load_pem_x509_certificate(cert_bytes)
delta = cert.not_valid_after_utc - djangotime.now()
redis_url = f"redis://{settings.REDIS_HOST}"
redis_ping = False
with suppress(Exception):
with from_url(redis_url) as conn:
conn.ping()
redis_ping = True
celery_queue_health = "healthy"
try:
queue_len = get_celery_queue_len()
except RuntimeError as e:
queue_len = -1
celery_queue_health = "unhealthy"
logger.error(f"Error getting celery queue length: {e}")
nats_std_port, nats_ws_port = get_nats_ports()
mesh_port = getattr(settings, "MESH_PORT", 4430)
ret = {
"version": settings.TRMM_VERSION,
"latest_agent_version": settings.LATEST_AGENT_VER,
"agent_count": Agent.objects.count(),
"client_count": Client.objects.count(),
"site_count": Site.objects.count(),
"disk_usage_percent": disk_usage,
"mem_usage_percent": mem_usage,
"days_until_cert_expires": delta.days,
"cert_expired": delta.days < 0,
"redis_ping": redis_ping,
"celery_queue_len": queue_len,
"celery_queue_health": celery_queue_health,
"nats_std_ping": localhost_port_is_open(nats_std_port),
"nats_ws_ping": localhost_port_is_open(nats_ws_port),
"mesh_ping": localhost_port_is_open(mesh_port),
"services_running": {
"mesh": sysd_svc_is_running("meshcentral.service"),
"daphne": sysd_svc_is_running("daphne.service"),
"celery": sysd_svc_is_running("celery.service"),
"celerybeat": sysd_svc_is_running("celerybeat.service"),
"redis": sysd_svc_is_running("redis-server.service"),
"nats": sysd_svc_is_running("nats.service"),
"nats-api": sysd_svc_is_running("nats-api.service"),
},
}
return JsonResponse(ret, json_dumps_params={"indent": 2})
# TODO deprecated
@csrf_exempt
@monitoring_view
def status(request):

View File

@@ -1,9 +1,10 @@
import json
import os
import re
import socket
import subprocess
import tempfile
import time
import re
from contextlib import contextmanager
from typing import TYPE_CHECKING, List, Literal, Optional, Union
from zoneinfo import ZoneInfo
@@ -21,6 +22,7 @@ from rest_framework.response import Response
from agents.models import Agent
from core.utils import get_core_settings, token_is_valid
from logs.models import DebugLog
from tacticalrmm.celery import app as celery_app
from tacticalrmm.constants import (
MONTH_DAYS,
MONTHS,
@@ -41,8 +43,8 @@ from tacticalrmm.helpers import (
)
if TYPE_CHECKING:
from clients.models import Client, Site
from alerts.models import Alert
from clients.models import Client, Site
def generate_winagent_exe(
@@ -468,3 +470,21 @@ def runcmd_placeholder_text() -> dict[str, str]:
),
}
return ret
def get_celery_queue_len():
try:
with celery_app.pool.acquire(block=True) as conn:
return conn.default_channel.client.llen("celery")
except Exception as e:
raise RuntimeError(f"Error getting celery queue length: {e}")
def localhost_port_is_open(port):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1)
s.connect(("127.0.0.1", port))
return True
except (socket.timeout, ConnectionRefusedError):
return False