From 03b2982128d7ba27757643b5da10db705dd15874 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Thu, 28 Jan 2021 23:11:32 +0000 Subject: [PATCH 01/10] update build flags --- api/tacticalrmm/agents/views.py | 2 +- api/tacticalrmm/clients/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tacticalrmm/agents/views.py b/api/tacticalrmm/agents/views.py index 7bb6d432d0..834453235c 100644 --- a/api/tacticalrmm/agents/views.py +++ b/api/tacticalrmm/agents/views.py @@ -473,7 +473,7 @@ def install_agent(request): f"GOARCH={goarch}", go_bin, "build", - f"-ldflags=\"-X 'main.Inno={inno}'", + f"-ldflags=\"-s -w -X 'main.Inno={inno}'", f"-X 'main.Api={api}'", f"-X 'main.Client={client_id}'", f"-X 'main.Site={site_id}'", diff --git a/api/tacticalrmm/clients/views.py b/api/tacticalrmm/clients/views.py index 6a10cc0abe..99c2efa5bb 100644 --- a/api/tacticalrmm/clients/views.py +++ b/api/tacticalrmm/clients/views.py @@ -223,7 +223,7 @@ def get(self, request, uid): f"GOARCH={goarch}", go_bin, "build", - f"-ldflags=\"-X 'main.Inno={inno}'", + f"-ldflags=\"-s -w -X 'main.Inno={inno}'", f"-X 'main.Api={api}'", f"-X 'main.Client={d.client.pk}'", f"-X 'main.Site={d.site.pk}'", From a65eb62a54c6fb2af8738bc8d2b24fa388a39634 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 00:34:18 +0000 Subject: [PATCH 02/10] checkrunner changes wh1te909/rmmagent@10a0935f1b75c0f43cbd1689f3f4f1f45a42beb6 --- api/tacticalrmm/checks/tests.py | 42 ++++++++++++++++++++++++++++++- api/tacticalrmm/checks/views.py | 14 +++++++++-- web/src/components/AgentTable.vue | 13 +++++++--- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/api/tacticalrmm/checks/tests.py b/api/tacticalrmm/checks/tests.py index 5b43d06fc2..64fbc164d2 100644 --- a/api/tacticalrmm/checks/tests.py +++ b/api/tacticalrmm/checks/tests.py @@ -2,9 +2,9 @@ from tacticalrmm.test import TacticalTestCase from .serializers import CheckSerializer from django.utils import timezone as djangotime +from unittest.mock import patch from model_bakery import baker -from itertools import cycle class TestCheckViews(TacticalTestCase): @@ -184,6 +184,46 @@ def test_edit_check_alert(self): self.check_not_authenticated("patch", url_a) + @patch("agents.models.Agent.nats_cmd") + def test_run_checks(self, nats_cmd): + agent = baker.make_recipe("agents.agent", version="1.4.1") + agent_old = baker.make_recipe("agents.agent", version="1.0.2") + agent_b4_141 = baker.make_recipe("agents.agent", version="1.4.0") + + url = f"/checks/runchecks/{agent_old.pk}/" + r = self.client.get(url) + self.assertEqual(r.status_code, 400) + self.assertEqual(r.json(), "Requires agent version 1.1.0 or greater") + + url = f"/checks/runchecks/{agent_b4_141.pk}/" + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + nats_cmd.assert_called_with({"func": "runchecks"}, wait=False) + + nats_cmd.reset_mock() + nats_cmd.return_value = "busy" + url = f"/checks/runchecks/{agent.pk}/" + r = self.client.get(url) + self.assertEqual(r.status_code, 400) + nats_cmd.assert_called_with({"func": "runchecks"}, timeout=15) + self.assertEqual(r.json(), f"Checks are already running on {agent.hostname}") + + nats_cmd.reset_mock() + nats_cmd.return_value = "ok" + url = f"/checks/runchecks/{agent.pk}/" + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + nats_cmd.assert_called_with({"func": "runchecks"}, timeout=15) + self.assertEqual(r.json(), f"Checks will now be re-run on {agent.hostname}") + + nats_cmd.reset_mock() + nats_cmd.return_value = "timeout" + url = f"/checks/runchecks/{agent.pk}/" + r = self.client.get(url) + self.assertEqual(r.status_code, 400) + nats_cmd.assert_called_with({"func": "runchecks"}, timeout=15) + self.assertEqual(r.json(), "Unable to contact the agent") + def test_get_check_history(self): # setup data agent = baker.make_recipe("agents.agent") diff --git a/api/tacticalrmm/checks/views.py b/api/tacticalrmm/checks/views.py index 2f601a8c79..3900d9bcf0 100644 --- a/api/tacticalrmm/checks/views.py +++ b/api/tacticalrmm/checks/views.py @@ -1,4 +1,5 @@ import asyncio +from packaging import version as pyver from django.shortcuts import get_object_or_404 from django.db.models import Q @@ -168,8 +169,17 @@ def run_checks(request, pk): if not agent.has_nats: return notify_error("Requires agent version 1.1.0 or greater") - asyncio.run(agent.nats_cmd({"func": "runchecks"}, wait=False)) - return Response(agent.hostname) + if pyver.parse(agent.version) >= pyver.parse("1.4.1"): + r = asyncio.run(agent.nats_cmd({"func": "runchecks"}, timeout=15)) + if r == "busy": + return notify_error(f"Checks are already running on {agent.hostname}") + elif r == "ok": + return Response(f"Checks will now be re-run on {agent.hostname}") + else: + return notify_error("Unable to contact the agent") + else: + asyncio.run(agent.nats_cmd({"func": "runchecks"}, wait=False)) + return Response(f"Checks will now be re-run on {agent.hostname}") @api_view() diff --git a/web/src/components/AgentTable.vue b/web/src/components/AgentTable.vue index 0b83d7f983..cbcc2e2f5e 100644 --- a/web/src/components/AgentTable.vue +++ b/web/src/components/AgentTable.vue @@ -541,10 +541,17 @@ export default { window.open(url, "", "scrollbars=no,location=no,status=no,toolbar=no,menubar=no,width=1280,height=826"); }, runChecks(pk) { - axios + this.$q.loading.show(); + this.$axios .get(`/checks/runchecks/${pk}/`) - .then(r => this.notifySuccess(`Checks will now be re-run on ${r.data}`)) - .catch(e => this.notifyError(e.response.data)); + .then(r => { + this.$q.loading.hide(); + this.notifySuccess(r.data); + }) + .catch(e => { + this.$q.loading.hide(); + this.notifyError(e.response.data); + }); }, removeAgent(pk, name) { this.$q From 415bff303a691f086a6e51eea71c37df5f56cee4 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 01:22:35 +0000 Subject: [PATCH 03/10] add some debug for unsupported agents --- api/tacticalrmm/agents/tasks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/tacticalrmm/agents/tasks.py b/api/tacticalrmm/agents/tasks.py index de1a5b6f48..64a8eb2492 100644 --- a/api/tacticalrmm/agents/tasks.py +++ b/api/tacticalrmm/agents/tasks.py @@ -19,9 +19,11 @@ def _check_agent_service(pk: int) -> None: agent = Agent.objects.get(pk=pk) r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=2)) + # if the agent is respoding to pong from the rpc service but is not showing as online (handled by tacticalagent service) + # then tacticalagent service is hung. forcefully restart it if r == "pong": logger.info( - f"Detected crashed tacticalagent service on {agent.hostname}, attempting recovery" + f"Detected crashed tacticalagent service on {agent.hostname} v{agent.version}, attempting recovery" ) data = {"func": "recover", "payload": {"mode": "tacagent"}} asyncio.run(agent.nats_cmd(data, wait=False)) @@ -109,6 +111,10 @@ def agent_update(pk: int) -> str: asyncio.run(agent.nats_cmd(nats_data, wait=False)) return "created" + else: + logger.warning( + f"{agent.hostname} v{agent.version} is running an unsupported version. Refusing to update." + ) return "not supported" From 39988a4c2f919cbaa4288141a4c3c0f4be2bd1e7 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 02:15:27 +0000 Subject: [PATCH 04/10] cleanup an old view --- api/tacticalrmm/agents/serializers.py | 6 ++++++ api/tacticalrmm/agents/tests.py | 26 ++------------------------ api/tacticalrmm/agents/views.py | 27 +++++++-------------------- api/tacticalrmm/checks/tests.py | 2 ++ web/src/components/AgentTable.vue | 8 ++++---- 5 files changed, 21 insertions(+), 48 deletions(-) diff --git a/api/tacticalrmm/agents/serializers.py b/api/tacticalrmm/agents/serializers.py index 6abea47723..34e4a92fff 100644 --- a/api/tacticalrmm/agents/serializers.py +++ b/api/tacticalrmm/agents/serializers.py @@ -34,6 +34,12 @@ class Meta: ] +class AgentOverdueActionSerializer(serializers.ModelSerializer): + class Meta: + model = Agent + fields = ["pk", "overdue_email_alert", "overdue_text_alert"] + + class AgentTableSerializer(serializers.ModelSerializer): patches_pending = serializers.ReadOnlyField(source="has_patches_pending") pending_actions = serializers.SerializerMethodField() diff --git a/api/tacticalrmm/agents/tests.py b/api/tacticalrmm/agents/tests.py index e42e994967..022bb2f09c 100644 --- a/api/tacticalrmm/agents/tests.py +++ b/api/tacticalrmm/agents/tests.py @@ -479,42 +479,20 @@ def test_by_site(self): def test_overdue_action(self): url = "/agents/overdueaction/" - payload = {"pk": self.agent.pk, "alertType": "email", "action": "enabled"} + payload = {"pk": self.agent.pk, "overdue_email_alert": True} r = self.client.post(url, payload, format="json") self.assertEqual(r.status_code, 200) agent = Agent.objects.get(pk=self.agent.pk) self.assertTrue(agent.overdue_email_alert) self.assertEqual(self.agent.hostname, r.data) - payload.update({"alertType": "email", "action": "disabled"}) - r = self.client.post(url, payload, format="json") - self.assertEqual(r.status_code, 200) - agent = Agent.objects.get(pk=self.agent.pk) - self.assertFalse(agent.overdue_email_alert) - self.assertEqual(self.agent.hostname, r.data) - - payload.update({"alertType": "text", "action": "enabled"}) - r = self.client.post(url, payload, format="json") - self.assertEqual(r.status_code, 200) - agent = Agent.objects.get(pk=self.agent.pk) - self.assertTrue(agent.overdue_text_alert) - self.assertEqual(self.agent.hostname, r.data) - - payload.update({"alertType": "text", "action": "disabled"}) + payload = {"pk": self.agent.pk, "overdue_text_alert": False} r = self.client.post(url, payload, format="json") self.assertEqual(r.status_code, 200) agent = Agent.objects.get(pk=self.agent.pk) self.assertFalse(agent.overdue_text_alert) self.assertEqual(self.agent.hostname, r.data) - payload.update({"alertType": "email", "action": "523423"}) - r = self.client.post(url, payload, format="json") - self.assertEqual(r.status_code, 400) - - payload.update({"alertType": "text", "action": "asdasd3434asdasd"}) - r = self.client.post(url, payload, format="json") - self.assertEqual(r.status_code, 400) - self.check_not_authenticated("post", url) def test_list_agents_no_detail(self): diff --git a/api/tacticalrmm/agents/views.py b/api/tacticalrmm/agents/views.py index 834453235c..92d5bc8dfe 100644 --- a/api/tacticalrmm/agents/views.py +++ b/api/tacticalrmm/agents/views.py @@ -30,6 +30,7 @@ AgentEditSerializer, NoteSerializer, NotesSerializer, + AgentOverdueActionSerializer, ) from winupdate.serializers import WinUpdatePolicySerializer @@ -333,26 +334,12 @@ def by_site(request, sitepk): @api_view(["POST"]) def overdue_action(request): - pk = request.data["pk"] - alert_type = request.data["alertType"] - action = request.data["action"] - agent = get_object_or_404(Agent, pk=pk) - if alert_type == "email" and action == "enabled": - agent.overdue_email_alert = True - agent.save(update_fields=["overdue_email_alert"]) - elif alert_type == "email" and action == "disabled": - agent.overdue_email_alert = False - agent.save(update_fields=["overdue_email_alert"]) - elif alert_type == "text" and action == "enabled": - agent.overdue_text_alert = True - agent.save(update_fields=["overdue_text_alert"]) - elif alert_type == "text" and action == "disabled": - agent.overdue_text_alert = False - agent.save(update_fields=["overdue_text_alert"]) - else: - return Response( - {"error": "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST - ) + agent = get_object_or_404(Agent, pk=request.data["pk"]) + serializer = AgentOverdueActionSerializer( + instance=agent, data=request.data, partial=True + ) + serializer.is_valid(raise_exception=True) + serializer.save() return Response(agent.hostname) diff --git a/api/tacticalrmm/checks/tests.py b/api/tacticalrmm/checks/tests.py index 64fbc164d2..4f2ec7ffd2 100644 --- a/api/tacticalrmm/checks/tests.py +++ b/api/tacticalrmm/checks/tests.py @@ -224,6 +224,8 @@ def test_run_checks(self, nats_cmd): nats_cmd.assert_called_with({"func": "runchecks"}, timeout=15) self.assertEqual(r.json(), "Unable to contact the agent") + self.check_not_authenticated("get", url) + def test_get_check_history(self): # setup data agent = baker.make_recipe("agents.agent") diff --git a/web/src/components/AgentTable.vue b/web/src/components/AgentTable.vue index cbcc2e2f5e..bb76cfd83a 100644 --- a/web/src/components/AgentTable.vue +++ b/web/src/components/AgentTable.vue @@ -644,14 +644,14 @@ export default { this.$store.dispatch("loadNotes", pk); }, overdueAlert(category, pk, alert_action) { + const db_field = category === "email" ? "overdue_email_alert" : "overdue_text_alert"; const action = alert_action ? "enabled" : "disabled"; const data = { pk: pk, - alertType: category, - action: action, + [db_field]: alert_action, }; const alertColor = alert_action ? "positive" : "warning"; - axios + this.$axios .post("/agents/overdueaction/", data) .then(r => { this.$q.notify({ @@ -660,7 +660,7 @@ export default { message: `Overdue ${category} alerts ${action} on ${r.data}`, }); }) - .catch(e => this.notifyError(e.response.data.error)); + .catch(() => this.notifyError("Something went wrong")); }, agentClass(status) { if (status === "offline") { From e80dc663acec904bbf9e7b06a95f3c26e3bbfaf3 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 02:22:06 +0000 Subject: [PATCH 05/10] remove unused func --- api/tacticalrmm/checks/models.py | 36 -------------------------------- 1 file changed, 36 deletions(-) diff --git a/api/tacticalrmm/checks/models.py b/api/tacticalrmm/checks/models.py index b85ec90af7..b03324fde5 100644 --- a/api/tacticalrmm/checks/models.py +++ b/api/tacticalrmm/checks/models.py @@ -445,42 +445,6 @@ def handle_checkv2(self, data): return self.status - def handle_check(self, data): - if self.check_type != "cpuload" and self.check_type != "memory": - - if data["status"] == "passing" and self.fail_count != 0: - self.fail_count = 0 - self.save(update_fields=["fail_count"]) - - elif data["status"] == "failing": - self.fail_count += 1 - self.save(update_fields=["fail_count"]) - - else: - self.history.append(data["percent"]) - - if len(self.history) > 15: - self.history = self.history[-15:] - - self.save(update_fields=["history"]) - - avg = int(mean(self.history)) - - if avg > self.threshold: - self.status = "failing" - self.fail_count += 1 - self.save(update_fields=["status", "fail_count"]) - else: - self.status = "passing" - if self.fail_count != 0: - self.fail_count = 0 - self.save(update_fields=["status", "fail_count"]) - else: - self.save(update_fields=["status"]) - - if self.email_alert and self.fail_count >= self.fails_b4_alert: - handle_check_email_alert_task.delay(self.pk) - @staticmethod def serialize(check): # serializes the check and returns json From 1731b05ad0d64b0cd4419e53f1586523cdbc0729 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 02:25:31 +0000 Subject: [PATCH 06/10] remove old serializers --- api/tacticalrmm/apiv3/views.py | 4 +- api/tacticalrmm/checks/serializers.py | 94 --------------------------- 2 files changed, 2 insertions(+), 96 deletions(-) diff --git a/api/tacticalrmm/apiv3/views.py b/api/tacticalrmm/apiv3/views.py index ada0457390..b61719371e 100644 --- a/api/tacticalrmm/apiv3/views.py +++ b/api/tacticalrmm/apiv3/views.py @@ -21,7 +21,7 @@ from accounts.models import User from winupdate.models import WinUpdatePolicy from software.models import InstalledSoftware -from checks.serializers import CheckRunnerGetSerializerV3 +from checks.serializers import CheckRunnerGetSerializer from agents.serializers import WinAgentSerializer from autotasks.serializers import TaskGOGetSerializer, TaskRunnerPatchSerializer from winupdate.serializers import ApprovedUpdateSerializer @@ -232,7 +232,7 @@ def get(self, request, agentid): ret = { "agent": agent.pk, "check_interval": agent.check_interval, - "checks": CheckRunnerGetSerializerV3(checks, many=True).data, + "checks": CheckRunnerGetSerializer(checks, many=True).data, } return Response(ret) diff --git a/api/tacticalrmm/checks/serializers.py b/api/tacticalrmm/checks/serializers.py index a39b2a9507..a3bec75ecc 100644 --- a/api/tacticalrmm/checks/serializers.py +++ b/api/tacticalrmm/checks/serializers.py @@ -95,101 +95,7 @@ class Meta: class CheckRunnerGetSerializer(serializers.ModelSerializer): - # for the windows agent # only send data needed for agent to run a check - - assigned_task = serializers.SerializerMethodField() - script = ScriptSerializer(read_only=True) - - def get_assigned_task(self, obj): - if obj.assignedtask.exists(): - # this will not break agents on version 0.10.2 or lower - # newer agents once released will properly handle multiple tasks assigned to a check - task = obj.assignedtask.first() - return AssignedTaskCheckRunnerField(task).data - - class Meta: - model = Check - exclude = [ - "policy", - "managed_by_policy", - "overriden_by_policy", - "parent_check", - "name", - "more_info", - "last_run", - "email_alert", - "text_alert", - "fails_b4_alert", - "fail_count", - "email_sent", - "text_sent", - "outage_history", - "extra_details", - "stdout", - "stderr", - "retcode", - "execution_time", - "svc_display_name", - "svc_policy_mode", - "created_by", - "created_time", - "modified_by", - "modified_time", - "history", - ] - - -class CheckRunnerGetSerializerV2(serializers.ModelSerializer): - # for the windows __python__ agent - # only send data needed for agent to run a check - - assigned_tasks = serializers.SerializerMethodField() - script = ScriptSerializer(read_only=True) - - def get_assigned_tasks(self, obj): - if obj.assignedtask.exists(): - tasks = obj.assignedtask.all() - return AssignedTaskCheckRunnerField(tasks, many=True).data - - class Meta: - model = Check - exclude = [ - "policy", - "managed_by_policy", - "overriden_by_policy", - "parent_check", - "name", - "more_info", - "last_run", - "email_alert", - "text_alert", - "fails_b4_alert", - "fail_count", - "email_sent", - "text_sent", - "outage_history", - "extra_details", - "stdout", - "stderr", - "retcode", - "execution_time", - "svc_display_name", - "svc_policy_mode", - "created_by", - "created_time", - "modified_by", - "modified_time", - "history", - ] - - -class CheckRunnerGetSerializerV3(serializers.ModelSerializer): - # for the windows __golang__ agent - # only send data needed for agent to run a check - # the difference here is in the script serializer - # script checks no longer rely on salt and are executed directly by the go agent - assigned_tasks = serializers.SerializerMethodField() script = ScriptCheckSerializer(read_only=True) From 25059de8e1a264a742c69631f274f86a6a915c2c Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 02:37:51 +0000 Subject: [PATCH 07/10] fix superseded windows defender updates --- api/tacticalrmm/apiv3/views.py | 2 ++ api/tacticalrmm/natsapi/views.py | 3 +++ api/tacticalrmm/winupdate/tasks.py | 4 ++++ api/tacticalrmm/winupdate/views.py | 2 ++ 4 files changed, 11 insertions(+) diff --git a/api/tacticalrmm/apiv3/views.py b/api/tacticalrmm/apiv3/views.py index b61719371e..ac74e1ccdc 100644 --- a/api/tacticalrmm/apiv3/views.py +++ b/api/tacticalrmm/apiv3/views.py @@ -332,6 +332,7 @@ def patch(self, request): update.installed = True update.save(update_fields=["result", "downloaded", "installed"]) + agent.delete_superseded_updates() return Response("ok") # agent calls this after it's finished installing all patches @@ -357,6 +358,7 @@ def post(self, request): f"{agent.hostname} is rebooting after updates were installed." ) + agent.delete_superseded_updates() return Response("ok") diff --git a/api/tacticalrmm/natsapi/views.py b/api/tacticalrmm/natsapi/views.py index f8aa18bf4e..5f0e1cd7e7 100644 --- a/api/tacticalrmm/natsapi/views.py +++ b/api/tacticalrmm/natsapi/views.py @@ -176,6 +176,7 @@ def put(self, request): asyncio.run(agent.nats_cmd({"func": "rebootnow"}, wait=False)) logger.info(f"{agent.hostname} is rebooting after updates were installed.") + agent.delete_superseded_updates() return Response("ok") def patch(self, request): @@ -199,6 +200,7 @@ def patch(self, request): u.result = "failed" u.save(update_fields=["result"]) + agent.delete_superseded_updates() return Response("ok") def post(self, request): @@ -233,4 +235,5 @@ def post(self, request): revision_number=update["revision_number"], ).save() + agent.delete_superseded_updates() return Response("ok") diff --git a/api/tacticalrmm/winupdate/tasks.py b/api/tacticalrmm/winupdate/tasks.py index 3a19fbcd91..54b45c4f9c 100644 --- a/api/tacticalrmm/winupdate/tasks.py +++ b/api/tacticalrmm/winupdate/tasks.py @@ -21,6 +21,7 @@ def auto_approve_updates_task(): agents = Agent.objects.only("pk", "version", "last_seen", "overdue_time") for agent in agents: + agent.delete_superseded_updates() try: agent.approve_updates() except: @@ -53,6 +54,7 @@ def check_agent_update_schedule_task(): ] for agent in online: + agent.delete_superseded_updates() install = False patch_policy = agent.get_patch_policy() @@ -126,6 +128,7 @@ def bulk_install_updates_task(pks: List[int]) -> None: chunks = (agents[i : i + 40] for i in range(0, len(agents), 40)) for chunk in chunks: for agent in chunk: + agent.delete_superseded_updates() nats_data = { "func": "installwinupdates", "guids": agent.get_approved_update_guids(), @@ -142,6 +145,7 @@ def bulk_check_for_updates_task(pks: List[int]) -> None: chunks = (agents[i : i + 40] for i in range(0, len(agents), 40)) for chunk in chunks: for agent in chunk: + agent.delete_superseded_updates() asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False)) time.sleep(0.05) time.sleep(15) diff --git a/api/tacticalrmm/winupdate/views.py b/api/tacticalrmm/winupdate/views.py index 67575ace75..4b5af2103f 100644 --- a/api/tacticalrmm/winupdate/views.py +++ b/api/tacticalrmm/winupdate/views.py @@ -22,6 +22,7 @@ def get_win_updates(request, pk): @api_view() def run_update_scan(request, pk): agent = get_object_or_404(Agent, pk=pk) + agent.delete_superseded_updates() if pyver.parse(agent.version) < pyver.parse("1.3.0"): return notify_error("Requires agent version 1.3.0 or greater") @@ -32,6 +33,7 @@ def run_update_scan(request, pk): @api_view() def install_updates(request, pk): agent = get_object_or_404(Agent, pk=pk) + agent.delete_superseded_updates() if pyver.parse(agent.version) < pyver.parse("1.3.0"): return notify_error("Requires agent version 1.3.0 or greater") From 4284493dceb623b70796dcb6ee7d17ad804ed027 Mon Sep 17 00:00:00 2001 From: Saulius Kazokas <9000854+saulens22@users.noreply.github.com> Date: Fri, 29 Jan 2021 07:10:10 +0200 Subject: [PATCH 08/10] Fix "TRMM Defender Exclusions" script shell type --- api/tacticalrmm/scripts/community_scripts.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tacticalrmm/scripts/community_scripts.json b/api/tacticalrmm/scripts/community_scripts.json index 05d309a9ad..043079f9ad 100644 --- a/api/tacticalrmm/scripts/community_scripts.json +++ b/api/tacticalrmm/scripts/community_scripts.json @@ -193,6 +193,6 @@ "submittedBy": "https://github.com/dinger1986", "name": "TRMM Defender Exclusions", "description": "Windows Defender Exclusions for Tactical RMM", - "shell": "cmd" + "shell": "powershell" } -] \ No newline at end of file +] From c25dc1b99cbee34fc00db949af5bfea4a7a5dc91 Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 07:39:08 +0000 Subject: [PATCH 09/10] also override shell during load community scripts --- api/tacticalrmm/scripts/models.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api/tacticalrmm/scripts/models.py b/api/tacticalrmm/scripts/models.py index b2748e0285..fd38132c60 100644 --- a/api/tacticalrmm/scripts/models.py +++ b/api/tacticalrmm/scripts/models.py @@ -72,6 +72,7 @@ def load_community_scripts(cls): i.name = script["name"] i.description = script["description"] i.category = "Community" + i.shell = script["shell"] with open(os.path.join(scripts_dir, script["filename"]), "rb") as f: script_bytes = ( @@ -80,7 +81,13 @@ def load_community_scripts(cls): i.code_base64 = base64.b64encode(script_bytes).decode("ascii") i.save( - update_fields=["name", "description", "category", "code_base64"] + update_fields=[ + "name", + "description", + "category", + "code_base64", + "shell", + ] ) else: print(f"Adding new community script: {script['name']}") From e17d25c15666df599f51a8a9f5e059e4032eeabd Mon Sep 17 00:00:00 2001 From: wh1te909 Date: Fri, 29 Jan 2021 08:12:03 +0000 Subject: [PATCH 10/10] bump versions --- api/tacticalrmm/tacticalrmm/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/tacticalrmm/tacticalrmm/settings.py b/api/tacticalrmm/tacticalrmm/settings.py index 3ccae64342..bad203876a 100644 --- a/api/tacticalrmm/tacticalrmm/settings.py +++ b/api/tacticalrmm/tacticalrmm/settings.py @@ -15,14 +15,14 @@ AUTH_USER_MODEL = "accounts.User" # latest release -TRMM_VERSION = "0.4.1" +TRMM_VERSION = "0.4.2" # bump this version everytime vue code is changed # to alert user they need to manually refresh their browser -APP_VER = "0.0.108" +APP_VER = "0.0.109" # https://github.com/wh1te909/rmmagent -LATEST_AGENT_VER = "1.4.0" +LATEST_AGENT_VER = "1.4.1" MESH_VER = "0.7.54"