diff --git a/api/tacticalrmm/core/decorators.py b/api/tacticalrmm/core/decorators.py index c77dde37fa..a022e29aaa 100644 --- a/api/tacticalrmm/core/decorators.py +++ b/api/tacticalrmm/core/decorators.py @@ -29,3 +29,24 @@ def wrap(request, *args, **kwargs): wrap.__doc__ = function.__doc__ wrap.__name__ = function.__name__ return wrap + +def metrics_view(function): + def wrap(request, *args, **kwargs): + if request.method != "GET": + return HttpResponse("Invalid request type\n", status=400) + + if "Authorization" not in request.headers: + return HttpResponse("Missing 'Authorization' header\n", status=400) + + token = getattr(settings, "MON_TOKEN", "") + if not token: + return HttpResponse("Missing token config\n", status=401) + + if request.headers["Authorization"] != 'Bearer ' + token: + return HttpResponse("Not authenticated\n", status=401) + + return function(request, *args, **kwargs) + + wrap.__doc__ = function.__doc__ + wrap.__name__ = function.__name__ + return wrap diff --git a/api/tacticalrmm/core/views.py b/api/tacticalrmm/core/views.py index b423067ec8..175c7a7b90 100644 --- a/api/tacticalrmm/core/views.py +++ b/api/tacticalrmm/core/views.py @@ -5,7 +5,7 @@ import pytz from cryptography import x509 from django.conf import settings -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from django.shortcuts import get_object_or_404 from django.utils import timezone as djangotime from django.views.decorators.csrf import csrf_exempt @@ -15,7 +15,7 @@ from rest_framework.response import Response from rest_framework.views import APIView -from core.decorators import monitoring_view +from core.decorators import monitoring_view, metrics_view from core.utils import get_core_settings, sysd_svc_is_running, token_is_valid from logs.models import AuditLog from tacticalrmm.constants import AuditActionType, PAStatus @@ -449,3 +449,45 @@ def status(request): "nginx": sysd_svc_is_running("nginx.service"), } return JsonResponse(ret, json_dumps_params={"indent": 2}) + +@csrf_exempt +@metrics_view +def metrics(request): + from agents.models import Agent + from clients.models import Client, Site + cert_file, _ = get_certs() + cert_bytes = Path(cert_file).read_bytes() + cert = x509.load_pem_x509_certificate(cert_bytes) + cert_expiration = cert.not_valid_after.timestamp() + metrics = [ + 'trmm_buildinfo{{version="{}"}} 1'.format(settings.TRMM_VERSION), + 'trmm_meshinfo{{version="{}"}} 1'.format(settings.MESH_VER), + 'trmm_natsinfo{{version="{}"}} 1'.format(settings.NATS_SERVER_VER), + 'trmm_appinfo{{version="{}"}} 1'.format(settings.APP_VER), + 'trmm_agent{{latest_version="{}"}} 1'.format(settings.LATEST_AGENT_VER), + "trmm_agent_count {}".format(Agent.objects.count()), + "trmm_client_count {}".format(Client.objects.count()), + "trmm_site_count {}".format(Site.objects.count()), + "trmm_cert_expiration_time {}".format(cert_expiration), + ] + + if settings.DOCKER_BUILD: + metrics.append("trmm_docker_build 1") + else: + metrics.append("trmm_docker_build 0") + services = { + "django": "rmm.service", + "mesh": "meshcentral.service", + "daphne": "daphne.service", + "celery": "celery.service", + "celerybeat": "celerybeat.service", + "redis": "redis-server.service", + "postgres": "postgresql.service", + "mongo": "mongod.service", + "nats": "nats.service", + "nats-api": "nats-api.service", + "nginx": "nginx.service", + } + for k,v in services.items(): + metrics.append('trmm_service{{name="{}",unit="{}"}} {}'.format(k,v,int(sysd_svc_is_running(v)))) + return HttpResponse(("\n").join(metrics),content_type="text/plain") diff --git a/api/tacticalrmm/tacticalrmm/urls.py b/api/tacticalrmm/tacticalrmm/urls.py index 9d74598681..f7ffffe4b9 100644 --- a/api/tacticalrmm/tacticalrmm/urls.py +++ b/api/tacticalrmm/tacticalrmm/urls.py @@ -5,6 +5,7 @@ from accounts.views import CheckCreds, LoginView from agents.consumers import SendCMD from core.consumers import DashInfo +from core import views as core_views class AgentIDConverter: @@ -32,6 +33,7 @@ def to_url(self, value): path("winupdate/", include("winupdate.urls")), path("software/", include("software.urls")), path("core/", include("core.urls")), + path("metrics", core_views.metrics), path("automation/", include("automation.urls")), path("tasks/", include("autotasks.urls")), path("logs/", include("logs.urls")),