Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e3ee91cef | ||
|
|
74721b6d61 | ||
|
|
2190943faf | ||
|
|
eb80f07b7d |
2
.github/workflows/ci-tests.yml
vendored
2
.github/workflows/ci-tests.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
- name: Codestyle black
|
- name: Codestyle black
|
||||||
working-directory: api
|
working-directory: api
|
||||||
run: |
|
run: |
|
||||||
black --exclude migrations/ --check tacticalrmm
|
black --exclude migrations/ --check --diff tacticalrmm
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import json
|
import json
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
# TODO deprecated
|
||||||
def monitoring_view(function):
|
def monitoring_view(function):
|
||||||
def wrap(request, *args, **kwargs):
|
def wrap(request, *args, **kwargs):
|
||||||
if request.method != "POST":
|
if request.method != "POST":
|
||||||
@@ -29,3 +31,25 @@ def monitoring_view(function):
|
|||||||
wrap.__doc__ = function.__doc__
|
wrap.__doc__ = function.__doc__
|
||||||
wrap.__name__ = function.__name__
|
wrap.__name__ = function.__name__
|
||||||
return wrap
|
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
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ urlpatterns = [
|
|||||||
path("urlaction/run/test/", views.RunTestURLAction.as_view()),
|
path("urlaction/run/test/", views.RunTestURLAction.as_view()),
|
||||||
path("smstest/", views.TwilioSMSTest.as_view()),
|
path("smstest/", views.TwilioSMSTest.as_view()),
|
||||||
path("clearcache/", views.clear_cache),
|
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("openai/generate/", views.OpenAICodeCompletion.as_view()),
|
||||||
path("webtermperms/", views.webterm_perms),
|
path("webtermperms/", views.webterm_perms),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from rest_framework.request import Request
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
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.tasks import sync_mesh_perms_task
|
||||||
from core.utils import (
|
from core.utils import (
|
||||||
get_core_settings,
|
get_core_settings,
|
||||||
@@ -32,6 +32,7 @@ from core.utils import (
|
|||||||
from logs.models import AuditLog
|
from logs.models import AuditLog
|
||||||
from tacticalrmm.constants import AuditActionType, PAStatus
|
from tacticalrmm.constants import AuditActionType, PAStatus
|
||||||
from tacticalrmm.helpers import get_certs, notify_error
|
from tacticalrmm.helpers import get_certs, notify_error
|
||||||
|
from tacticalrmm.logger import logger
|
||||||
from tacticalrmm.permissions import (
|
from tacticalrmm.permissions import (
|
||||||
_has_perm_on_agent,
|
_has_perm_on_agent,
|
||||||
_has_perm_on_client,
|
_has_perm_on_client,
|
||||||
@@ -557,6 +558,72 @@ class TwilioSMSTest(APIView):
|
|||||||
return Response(msg)
|
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
|
@csrf_exempt
|
||||||
@monitoring_view
|
@monitoring_view
|
||||||
def status(request):
|
def status(request):
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import re
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import TYPE_CHECKING, List, Literal, Optional, Union
|
from typing import TYPE_CHECKING, List, Literal, Optional, Union
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
@@ -21,6 +22,7 @@ from rest_framework.response import Response
|
|||||||
from agents.models import Agent
|
from agents.models import Agent
|
||||||
from core.utils import get_core_settings, token_is_valid
|
from core.utils import get_core_settings, token_is_valid
|
||||||
from logs.models import DebugLog
|
from logs.models import DebugLog
|
||||||
|
from tacticalrmm.celery import app as celery_app
|
||||||
from tacticalrmm.constants import (
|
from tacticalrmm.constants import (
|
||||||
MONTH_DAYS,
|
MONTH_DAYS,
|
||||||
MONTHS,
|
MONTHS,
|
||||||
@@ -41,8 +43,8 @@ from tacticalrmm.helpers import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from clients.models import Client, Site
|
|
||||||
from alerts.models import Alert
|
from alerts.models import Alert
|
||||||
|
from clients.models import Client, Site
|
||||||
|
|
||||||
|
|
||||||
def generate_winagent_exe(
|
def generate_winagent_exe(
|
||||||
@@ -468,3 +470,21 @@ def runcmd_placeholder_text() -> dict[str, str]:
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
return ret
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user