Compare commits

...

26 Commits

Author SHA1 Message Date
wh1te909
0d2b4af986 Release 0.2.10 2020-12-10 10:34:40 +00:00
wh1te909
dc2b2eeb9f bump versions 2020-12-10 10:33:44 +00:00
wh1te909
e5dbb66d53 cleanup agent update func 2020-12-10 10:31:58 +00:00
wh1te909
3474b1c471 fix failing checks alert 2020-12-10 00:01:54 +00:00
wh1te909
3886de5b7c add postgres vacuum 2020-12-10 00:00:02 +00:00
wh1te909
2b3cec06b3 Release 0.2.9 2020-12-09 05:07:11 +00:00
wh1te909
8536754d14 bump version for new agent 2020-12-09 05:06:19 +00:00
wh1te909
1f36235801 fix wording 2020-12-09 05:04:25 +00:00
wh1te909
a4194b14f9 Release 0.2.8 2020-12-09 00:50:48 +00:00
wh1te909
2dcc629d9d bump versions 2020-12-09 00:31:33 +00:00
wh1te909
98ddadc6bc add sync task 2020-12-08 23:02:05 +00:00
wh1te909
f6e47b7383 remove extra services view 2020-12-08 20:09:09 +00:00
wh1te909
f073ddc906 Release 0.2.7 2020-12-07 09:50:37 +00:00
wh1te909
3e00631925 cleanup older pending action agent updates if one exists with an older agent version 2020-12-07 09:50:15 +00:00
wh1te909
9b7ac58562 Release 0.2.6 2020-12-07 08:56:20 +00:00
wh1te909
f242ddd801 bump versions 2020-12-07 08:55:49 +00:00
wh1te909
c129886fe2 change sleeps 2020-12-07 08:30:21 +00:00
wh1te909
f577e814cf add refresh summary 2020-12-07 08:29:37 +00:00
wh1te909
c860a0cedd update reqs 2020-12-07 00:35:38 +00:00
wh1te909
ae7e28e492 try fixing coveralls branch 2020-12-06 00:43:36 +00:00
wh1te909
90a63234ad add coveralls 2020-12-04 06:40:44 +00:00
wh1te909
14bca52e8f remove dead code, update middleware 2020-12-04 06:25:53 +00:00
wh1te909
2f3c3361cf remove static clients list from audit log 2020-12-04 06:05:25 +00:00
wh1te909
4034134055 add task scheduler expire after wh1te909/rmmagent@fe91e5f110 2020-12-03 22:46:25 +00:00
sadnub
c04f94cb7b fix certificates on docker 2020-12-03 12:29:03 -05:00
sadnub
fd1bbc7925 Update docker-build-push.yml 2020-12-02 07:53:12 -05:00
31 changed files with 626 additions and 723 deletions

View File

@@ -35,7 +35,7 @@ jobs:
pull: true
file: ./docker/containers/tactical/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical:${{ steps.prep.outputs.version }},sadnub/tactical:latest
tags: tacticalrmm/tactical:${{ steps.prep.outputs.version }},tacticalrmm/tactical:latest
- name: Build and Push Tactical MeshCentral Image
uses: docker/build-push-action@v2
@@ -45,7 +45,7 @@ jobs:
pull: true
file: ./docker/containers/tactical-meshcentral/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical-meshcentral:${{ steps.prep.outputs.version }},sadnub/tactical-meshcentral:latest
tags: tacticalrmm/tactical-meshcentral:${{ steps.prep.outputs.version }},tacticalrmm/tactical-meshcentral:latest
- name: Build and Push Tactical NATS Image
uses: docker/build-push-action@v2
@@ -55,7 +55,7 @@ jobs:
pull: true
file: ./docker/containers/tactical-nats/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical-nats:${{ steps.prep.outputs.version }},sadnub/tactical-nats:latest
tags: tacticalrmm/tactical-nats:${{ steps.prep.outputs.version }},tacticalrmm/tactical-nats:latest
- name: Build and Push Tactical Salt Image
uses: docker/build-push-action@v2
@@ -65,7 +65,7 @@ jobs:
pull: true
file: ./docker/containers/tactical-salt/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical-salt:${{ steps.prep.outputs.version }},sadnub/tactical-salt:latest
tags: tacticalrmm/tactical-salt:${{ steps.prep.outputs.version }},tacticalrmm/tactical-salt:latest
- name: Build and Push Tactical Frontend Image
uses: docker/build-push-action@v2
@@ -75,7 +75,7 @@ jobs:
pull: true
file: ./docker/containers/tactical-frontend/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical-frontend:${{ steps.prep.outputs.version }},sadnub/tactical-frontend:latest
tags: tacticalrmm/tactical-frontend:${{ steps.prep.outputs.version }},tacticalrmm/tactical-frontend:latest
- name: Build and Push Tactical Nginx Image
uses: docker/build-push-action@v2
@@ -85,4 +85,4 @@ jobs:
pull: true
file: ./docker/containers/tactical-nginx/dockerfile
platforms: linux/amd64
tags: tacticalrmm/tactical-nginx:${{ steps.prep.outputs.version }},sadnub/tactical-nginx:latest
tags: tacticalrmm/tactical-nginx:${{ steps.prep.outputs.version }},tacticalrmm/tactical-nginx:latest

View File

@@ -20,6 +20,5 @@ omit =
*/urls.py
*/tests.py
*/test.py
api/*.py
checks/utils.py

View File

@@ -164,13 +164,11 @@ class Agent(BaseAuditModel):
elif i.status == "failing":
failing += 1
has_failing_checks = True if failing > 0 else False
ret = {
"total": total,
"passing": passing,
"failing": failing,
"has_failing_checks": has_failing_checks,
"has_failing_checks": failing > 0,
}
return ret
@@ -546,6 +544,7 @@ class Agent(BaseAuditModel):
ret = AgentEditSerializer(agent).data
del ret["all_timezones"]
del ret["client"]
return ret
@staticmethod

View File

@@ -1,8 +1,10 @@
import asyncio
from loguru import logger
from time import sleep
import random
import requests
from packaging import version as pyver
from typing import List
from django.conf import settings
@@ -13,137 +15,90 @@ from logs.models import PendingAction
logger.configure(**settings.LOG_CONFIG)
OLD_64_PY_AGENT = "https://github.com/wh1te909/winagent/releases/download/v0.11.2/winagent-v0.11.2.exe"
OLD_32_PY_AGENT = "https://github.com/wh1te909/winagent/releases/download/v0.11.2/winagent-v0.11.2-x86.exe"
def agent_update(pk: int) -> str:
agent = Agent.objects.get(pk=pk)
# skip if we can't determine the arch
if agent.arch is None:
logger.warning(f"Unable to determine arch on {agent.hostname}. Skipping.")
return "noarch"
if agent.has_nats:
if agent.pendingactions.filter(
action_type="agentupdate", status="pending"
).exists():
action = agent.pendingactions.filter(
action_type="agentupdate", status="pending"
).last()
if pyver.parse(action.details["version"]) < pyver.parse(
settings.LATEST_AGENT_VER
):
action.delete()
else:
return "pending"
PendingAction.objects.create(
agent=agent,
action_type="agentupdate",
details={
"url": agent.winagent_dl,
"version": settings.LATEST_AGENT_VER,
"inno": agent.win_inno_exe,
},
)
return "created"
# TODO
# Salt is deprecated, remove this once salt is gone
else:
agent.salt_api_async(
func="win_agent.do_agent_update_v2",
kwargs={
"inno": agent.win_inno_exe,
"url": agent.winagent_dl,
},
)
return "salt"
@app.task
def send_agent_update_task(pks, version):
assert isinstance(pks, list)
def send_agent_update_task(pks: List[int], version: str) -> None:
q = Agent.objects.filter(pk__in=pks)
agents = [i.pk for i in q if pyver.parse(i.version) < pyver.parse(version)]
agents: List[int] = [
i.pk for i in q if pyver.parse(i.version) < pyver.parse(version)
]
chunks = (agents[i : i + 30] for i in range(0, len(agents), 30))
for chunk in chunks:
for pk in chunk:
agent = Agent.objects.get(pk=pk)
# skip if we can't determine the arch
if agent.arch is None:
logger.warning(
f"Unable to determine arch on {agent.salt_id}. Skipping."
)
continue
# golang agent only backwards compatible with py agent 0.11.2
# force an upgrade to the latest python agent if version < 0.11.2
if pyver.parse(agent.version) < pyver.parse("0.11.2"):
url = OLD_64_PY_AGENT if agent.arch == "64" else OLD_32_PY_AGENT
inno = (
"winagent-v0.11.2.exe"
if agent.arch == "64"
else "winagent-v0.11.2-x86.exe"
)
else:
url = agent.winagent_dl
inno = agent.win_inno_exe
if agent.has_nats:
if agent.pendingactions.filter(
action_type="agentupdate", status="pending"
).exists():
continue
PendingAction.objects.create(
agent=agent,
action_type="agentupdate",
details={
"url": agent.winagent_dl,
"version": settings.LATEST_AGENT_VER,
"inno": agent.win_inno_exe,
},
)
# TODO
# Salt is deprecated, remove this once salt is gone
else:
r = agent.salt_api_async(
func="win_agent.do_agent_update_v2",
kwargs={
"inno": inno,
"url": url,
},
)
sleep(5)
for pk in agents:
agent_update(pk)
@app.task
def auto_self_agent_update_task():
def auto_self_agent_update_task() -> None:
core = CoreSettings.objects.first()
if not core.agent_auto_update:
logger.info("Agent auto update is disabled. Skipping.")
return
q = Agent.objects.only("pk", "version")
agents = [
pks: List[int] = [
i.pk
for i in q
if pyver.parse(i.version) < pyver.parse(settings.LATEST_AGENT_VER)
]
chunks = (agents[i : i + 30] for i in range(0, len(agents), 30))
for pk in pks:
agent_update(pk)
for chunk in chunks:
for pk in chunk:
agent = Agent.objects.get(pk=pk)
# skip if we can't determine the arch
if agent.arch is None:
logger.warning(
f"Unable to determine arch on {agent.salt_id}. Skipping."
)
continue
# golang agent only backwards compatible with py agent 0.11.2
# force an upgrade to the latest python agent if version < 0.11.2
if pyver.parse(agent.version) < pyver.parse("0.11.2"):
url = OLD_64_PY_AGENT if agent.arch == "64" else OLD_32_PY_AGENT
inno = (
"winagent-v0.11.2.exe"
if agent.arch == "64"
else "winagent-v0.11.2-x86.exe"
)
else:
url = agent.winagent_dl
inno = agent.win_inno_exe
if agent.has_nats:
if agent.pendingactions.filter(
action_type="agentupdate", status="pending"
).exists():
continue
PendingAction.objects.create(
agent=agent,
action_type="agentupdate",
details={
"url": agent.winagent_dl,
"version": settings.LATEST_AGENT_VER,
"inno": agent.win_inno_exe,
},
)
# TODO
# Salt is deprecated, remove this once salt is gone
else:
r = agent.salt_api_async(
func="win_agent.do_agent_update_v2",
kwargs={
"inno": inno,
"url": url,
},
)
sleep(5)
@app.task
def sync_sysinfo_task():
agents = Agent.objects.all()
online = [
i
for i in agents
if pyver.parse(i.version) >= pyver.parse("1.1.3") and i.status == "online"
]
for agent in online:
asyncio.run(agent.nats_cmd({"func": "sync"}, wait=False))
@app.task

View File

@@ -5,19 +5,20 @@ from unittest.mock import patch
from model_bakery import baker
from itertools import cycle
from django.test import TestCase, override_settings
from django.conf import settings
from django.utils import timezone as djangotime
from logs.models import PendingAction
from tacticalrmm.test import TacticalTestCase
from .serializers import AgentSerializer
from winupdate.serializers import WinUpdatePolicySerializer
from .models import Agent
from .tasks import (
agent_recovery_sms_task,
auto_self_agent_update_task,
sync_salt_modules_task,
batch_sync_modules_task,
OLD_64_PY_AGENT,
OLD_32_PY_AGENT,
)
from winupdate.models import WinUpdatePolicy
@@ -786,6 +787,70 @@ class TestAgentTasks(TacticalTestCase):
self.assertEqual(ret.status, "SUCCESS")
@patch("agents.models.Agent.salt_api_async")
def test_agent_update(self, salt_api_async):
from agents.tasks import agent_update
agent_noarch = baker.make_recipe(
"agents.agent",
operating_system="Error getting OS",
version="1.1.0",
)
r = agent_update(agent_noarch.pk)
self.assertEqual(r, "noarch")
self.assertEqual(
PendingAction.objects.filter(
agent=agent_noarch, action_type="agentupdate"
).count(),
0,
)
agent64_nats = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.1.0",
)
r = agent_update(agent64_nats.pk)
self.assertEqual(r, "created")
action = PendingAction.objects.get(agent__pk=agent64_nats.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
self.assertEqual(action.details["url"], settings.DL_64)
self.assertEqual(
action.details["inno"], f"winagent-v{settings.LATEST_AGENT_VER}.exe"
)
self.assertEqual(action.details["version"], settings.LATEST_AGENT_VER)
agent64_salt = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.0.0",
)
salt_api_async.return_value = True
r = agent_update(agent64_salt.pk)
self.assertEqual(r, "salt")
salt_api_async.assert_called_with(
func="win_agent.do_agent_update_v2",
kwargs={
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
"url": settings.DL_64,
},
)
salt_api_async.reset_mock()
agent32_nats = baker.make_recipe(
"agents.agent",
operating_system="Windows 7 Professional, 32 bit (build 7601.23964)",
version="1.1.0",
)
agent32_salt = baker.make_recipe(
"agents.agent",
operating_system="Windows 7 Professional, 32 bit (build 7601.23964)",
version="1.0.0",
)
""" @patch("agents.models.Agent.salt_api_async")
@patch("agents.tasks.sleep", return_value=None)
def test_auto_self_agent_update_task(self, mock_sleep, salt_api_async):
# test 64bit golang agent
@@ -888,4 +953,4 @@ class TestAgentTasks(TacticalTestCase):
"url": OLD_32_PY_AGENT,
},
)
self.assertEqual(ret.status, "SUCCESS")
self.assertEqual(ret.status, "SUCCESS") """

View File

@@ -30,4 +30,5 @@ urlpatterns = [
path("bulk/", views.bulk),
path("agent_counts/", views.agent_counts),
path("maintenance/", views.agent_maintenance),
path("<int:pk>/wmi/", views.WMI.as_view()),
]

View File

@@ -402,6 +402,9 @@ class Reboot(APIView):
},
}
if pyver.parse(agent.version) >= pyver.parse("1.1.2"):
nats_data["schedtaskpayload"]["deleteafter"] = True
r = asyncio.run(agent.nats_cmd(nats_data, timeout=10))
if r != "ok":
return notify_error(r)
@@ -895,3 +898,15 @@ def agent_maintenance(request):
return notify_error("Invalid data")
return Response("ok")
class WMI(APIView):
def get(self, request, pk):
agent = get_object_or_404(Agent, pk=pk)
if pyver.parse(agent.version) < pyver.parse("1.1.2"):
return notify_error("Requires agent version 1.1.2 or greater")
r = asyncio.run(agent.nats_cmd({"func": "sysinfo"}, timeout=20))
if r != "ok":
return notify_error("Unable to contact the agent")
return Response("ok")

View File

@@ -5,6 +5,7 @@ from tacticalrmm.celery import app
from django.conf import settings
import pytz
from django.utils import timezone as djangotime
from packaging import version as pyver
from .models import AutomatedTask
from logs.models import PendingAction
@@ -56,6 +57,11 @@ def create_win_task_schedule(pk, pending_action=False):
},
}
if task.remove_if_not_scheduled and pyver.parse(
task.agent.version
) >= pyver.parse("1.1.2"):
nats_data["schedtaskpayload"]["deleteafter"] = True
elif task.task_type == "checkfailure" or task.task_type == "manual":
nats_data = {
"func": "schedtask",
@@ -201,6 +207,7 @@ def remove_orphaned_win_tasks(agentpk):
"TacticalRMM_fixmesh",
"TacticalRMM_SchedReboot",
"TacticalRMM_sync",
"TacticalRMM_agentupdate",
)
for task in r:

View File

@@ -38,7 +38,6 @@ class Client(BaseAuditModel):
@property
def has_failing_checks(self):
agents = (
Agent.objects.only(
"pk",
@@ -50,14 +49,17 @@ class Client(BaseAuditModel):
.filter(site__client=self)
.prefetch_related("agentchecks")
)
failing = 0
for agent in agents:
if agent.checks["has_failing_checks"]:
return True
failing += 1
if agent.overdue_email_alert or agent.overdue_text_alert:
return agent.status == "overdue"
if agent.status == "overdue":
failing += 1
return False
return failing > 0
@staticmethod
def serialize(client):
@@ -98,7 +100,6 @@ class Site(BaseAuditModel):
@property
def has_failing_checks(self):
agents = (
Agent.objects.only(
"pk",
@@ -110,14 +111,17 @@ class Site(BaseAuditModel):
.filter(site=self)
.prefetch_related("agentchecks")
)
failing = 0
for agent in agents:
if agent.checks["has_failing_checks"]:
return True
failing += 1
if agent.overdue_email_alert or agent.overdue_text_alert:
return agent.status == "overdue"
if agent.status == "overdue":
failing += 1
return False
return failing > 0
@staticmethod
def serialize(site):

View File

@@ -1,14 +1,14 @@
amqp==2.6.1
asgiref==3.3.0
asgiref==3.3.1
asyncio-nats-client==0.11.4
billiard==3.6.3.0
celery==4.4.6
certifi==2020.11.8
certifi==2020.12.5
cffi==1.14.3
chardet==3.0.4
cryptography==3.2.1
decorator==4.4.2
Django==3.1.3
Django==3.1.4
django-cors-headers==3.5.0
django-rest-knox==4.1.0
djangorestframework==3.12.2
@@ -26,12 +26,11 @@ pyparsing==2.4.7
pytz==2020.4
qrcode==6.1
redis==3.5.3
requests==2.24.0
requests==2.25.0
six==1.15.0
sqlparse==0.4.1
tldextract==3.0.2
twilio==6.47.0
urllib3==1.25.11
twilio==6.49.0
urllib3==1.26.2
uWSGI==2.0.19.1
validators==0.18.1
vine==1.3.0

View File

@@ -9,21 +9,6 @@ class TestServiceViews(TacticalTestCase):
def setUp(self):
self.authenticate()
def test_get_services(self):
# test a call where agent doesn't exist
resp = self.client.get("/services/500/services/", format="json")
self.assertEqual(resp.status_code, 404)
agent = baker.make_recipe("agents.agent_with_services")
url = f"/services/{agent.pk}/services/"
serializer = ServicesSerializer(agent)
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(serializer.data, resp.data)
self.check_not_authenticated("get", url)
def test_default_services(self):
url = "/services/defaultservices/"
resp = self.client.get(url, format="json")
@@ -33,13 +18,13 @@ class TestServiceViews(TacticalTestCase):
self.check_not_authenticated("get", url)
@patch("agents.models.Agent.nats_cmd")
def test_get_refreshed_services(self, nats_cmd):
def test_get_services(self, nats_cmd):
# test a call where agent doesn't exist
resp = self.client.get("/services/500/refreshedservices/", format="json")
resp = self.client.get("/services/500/services/", format="json")
self.assertEqual(resp.status_code, 404)
agent = baker.make_recipe("agents.agent_with_services")
url = f"/services/{agent.pk}/refreshedservices/"
url = f"/services/{agent.pk}/services/"
nats_return = [
{

View File

@@ -4,7 +4,6 @@ from . import views
urlpatterns = [
path("<int:pk>/services/", views.get_services),
path("defaultservices/", views.default_services),
path("<int:pk>/refreshedservices/", views.get_refreshed_services),
path("serviceaction/", views.service_action),
path("<int:pk>/<svcname>/servicedetail/", views.service_detail),
path("editservice/", views.edit_service),

View File

@@ -19,17 +19,6 @@ logger.configure(**settings.LOG_CONFIG)
@api_view()
def get_services(request, pk):
agent = get_object_or_404(Agent, pk=pk)
return Response(ServicesSerializer(agent).data)
@api_view()
def default_services(request):
return Response(Check.load_default_services())
@api_view()
def get_refreshed_services(request, pk):
agent = get_object_or_404(Agent, pk=pk)
if not agent.has_nats:
return notify_error("Requires agent version 1.1.0 or greater")
@@ -43,6 +32,11 @@ def get_refreshed_services(request, pk):
return Response(ServicesSerializer(agent).data)
@api_view()
def default_services(request):
return Response(Check.load_default_services())
@api_view(["POST"])
def service_action(request):
agent = get_object_or_404(Agent, pk=request.data["pk"])

View File

@@ -41,6 +41,10 @@ app.conf.beat_schedule = {
"task": "agents.tasks.auto_self_agent_update_task",
"schedule": crontab(minute=35, hour="*"),
},
"agents-sync": {
"task": "agents.tasks.sync_sysinfo_task",
"schedule": crontab(minute=55, hour="*"),
},
}

View File

@@ -16,16 +16,15 @@ def get_debug_info():
EXCLUDE_PATHS = (
"/api/v3",
"/api/v2",
"/api/v1",
"/logs/auditlogs",
"/winupdate/winupdater",
"/winupdate/results",
f"/{settings.ADMIN_URL}",
"/logout",
"/agents/installagent",
"/logs/downloadlog",
)
ENDS_WITH = "/services/"
class AuditMiddleware:
def __init__(self, get_response):
@@ -37,7 +36,9 @@ class AuditMiddleware:
return response
def process_view(self, request, view_func, view_args, view_kwargs):
if not request.path.startswith(EXCLUDE_PATHS):
if not request.path.startswith(EXCLUDE_PATHS) and not request.path.endswith(
ENDS_WITH
):
# https://stackoverflow.com/questions/26240832/django-and-middleware-which-uses-request-user-is-always-anonymous
try:
# DRF saves the class of the view function as the .cls property

View File

@@ -15,25 +15,25 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User"
# latest release
TRMM_VERSION = "0.2.5"
TRMM_VERSION = "0.2.10"
# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.95"
APP_VER = "0.0.98"
# https://github.com/wh1te909/salt
LATEST_SALT_VER = "1.1.0"
# https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.1.1"
LATEST_AGENT_VER = "1.1.5"
MESH_VER = "0.7.10"
MESH_VER = "0.7.14"
SALT_MASTER_VER = "3002.2"
# for the update script, bump when need to recreate venv or npm install
PIP_VER = "3"
NPM_VER = "2"
PIP_VER = "4"
NPM_VER = "3"
DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe"
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"

View File

@@ -2,6 +2,7 @@ import json
import os
import string
import subprocess
import time
from typing import List, Dict
from loguru import logger
@@ -117,6 +118,7 @@ def reload_nats():
json.dump(config, f)
if not settings.DOCKER_BUILD:
time.sleep(0.5)
subprocess.run(
["/usr/local/bin/nats-server", "-signal", "reload"], capture_output=True
)

View File

@@ -107,7 +107,7 @@ def check_agent_update_schedule_task():
def check_for_updates_task(pk, wait=False, auto_approve=False):
if wait:
sleep(70)
sleep(120)
agent = Agent.objects.get(pk=pk)
ret = agent.salt_api_cmd(

View File

@@ -1,12 +1,9 @@
from django.urls import path
from . import views
from apiv3 import views as v3_views
urlpatterns = [
path("<int:pk>/getwinupdates/", views.get_win_updates),
path("<int:pk>/runupdatescan/", views.run_update_scan),
path("editpolicy/", views.edit_policy),
path("winupdater/", views.win_updater),
path("results/", v3_views.WinUpdater.as_view()),
path("<int:pk>/installnow/", views.install_updates),
]

View File

@@ -58,20 +58,3 @@ def edit_policy(request):
patch.action = request.data["policy"]
patch.save(update_fields=["action"])
return Response("ok")
@api_view()
@authentication_classes((TokenAuthentication,))
@permission_classes((IsAuthenticated,))
def win_updater(request):
agent = get_object_or_404(Agent, agent_id=request.data["agent_id"])
agent.delete_superseded_updates()
patches = (
WinUpdate.objects.filter(agent=agent)
.exclude(installed=True)
.filter(action="approve")
)
if patches:
return Response(ApprovedUpdateSerializer(patches, many=True).data)
return Response("nopatches")

View File

@@ -27,15 +27,21 @@ jobs:
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==49.6.0 wheel==0.35.1
pip install --no-cache-dir setuptools==50.3.2 wheel==0.36.1
pip install --no-cache-dir -r requirements.txt -r requirements-test.txt
displayName: "Install Python Dependencies"
- script: |
cd /myagent/_work/1/s/api
git config user.email "admin@example.com"
git config user.name "Bob"
git fetch
git checkout develop
git pull
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
python manage.py test -v 2
coverage run manage.py test -v 2
coveralls
displayName: "Run django tests"
- script: |

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="4"
SCRIPT_VERSION="5"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
GREEN='\033[0;32m'
@@ -50,6 +50,11 @@ if [ -d /meshcentral/meshcentral-coredumps ]; then
rm -f /meshcentral/meshcentral-coredumps/*
fi
printf >&2 "${GREEN}Running postgres vacuum${NC}\n"
sudo -u postgres psql -d tacticalrmm -c "vacuum full logs_auditlog"
sudo -u postgres psql -d tacticalrmm -c "vacuum full logs_pendingaction"
sudo -u postgres psql -d tacticalrmm -c "vacuum full agents_agentoutage"
dt_now=$(date '+%Y_%m_%d__%H_%M_%S')
tmp_dir=$(mktemp -d -t tacticalrmm-XXXXXXXXXXXXXXXXXXXXX)
sysd="/etc/systemd/system"

View File

@@ -70,7 +70,7 @@ DEBUG = False
DOCKER_BUILD = True
CERT_FILE = '/opt/tactical/certs/fullchain.pem'
CERT_KEY = '/opt/tactical/certs/privkey.pem'
KEY_FILE = '/opt/tactical/certs/privkey.pem'
SCRIPTS_DIR = '/opt/tactical/scripts'
@@ -180,4 +180,4 @@ fi
if [ "$1" = 'tactical-celerywinupdate' ]; then
check_tactical_ready
celery -A tacticalrmm worker -Q wupdate
fi
fi

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="26"
SCRIPT_VERSION="27"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
GREEN='\033[0;32m'
@@ -352,7 +352,7 @@ python3 -m venv env
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==49.6.0 wheel==0.35.1
pip install --no-cache-dir setuptools==50.3.2 wheel==0.36.1
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input

View File

@@ -7,7 +7,7 @@ pgpw="hunter2"
#####################################################
SCRIPT_VERSION="9"
SCRIPT_VERSION="10"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
GREEN='\033[0;32m'
@@ -246,7 +246,7 @@ python3 -m venv env
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==49.6.0 wheel==0.35.1
pip install --no-cache-dir setuptools==50.3.2 wheel==0.36.1
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
python manage.py collectstatic --no-input
python manage.py reload_nats

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="98"
SCRIPT_VERSION="100"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh'
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
YELLOW='\033[1;33m'
@@ -177,6 +177,11 @@ sudo cp /rmm/api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/
sudo chown ${USER}:${USER} /usr/local/bin/goversioninfo
sudo chmod +x /usr/local/bin/goversioninfo
printf >&2 "${GREEN}Running postgres vacuum${NC}\n"
sudo -u postgres psql -d tacticalrmm -c "vacuum full logs_auditlog"
sudo -u postgres psql -d tacticalrmm -c "vacuum full logs_pendingaction"
sudo -u postgres psql -d tacticalrmm -c "vacuum full agents_agentoutage"
if [[ "${CURRENT_PIP_VER}" != "${LATEST_PIP_VER}" ]]; then
rm -rf /rmm/api/env
cd /rmm/api
@@ -184,7 +189,7 @@ if [[ "${CURRENT_PIP_VER}" != "${LATEST_PIP_VER}" ]]; then
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==49.6.0 wheel==0.35.1
pip install --no-cache-dir setuptools==50.3.2 wheel==0.36.1
pip install --no-cache-dir -r requirements.txt
else
source /rmm/api/env/bin/activate

856
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,10 +13,10 @@
"axios": "^0.21.0",
"dotenv": "^8.2.0",
"qrcode.vue": "^1.7.0",
"quasar": "^1.14.5"
"quasar": "^1.14.6"
},
"devDependencies": {
"@quasar/app": "^2.1.8",
"@quasar/app": "^2.1.9",
"@quasar/cli": "^1.1.2",
"@quasar/quasar-app-extension-testing": "^1.0.0",
"@quasar/quasar-app-extension-testing-unit-jest": "^1.0.1",

View File

@@ -13,7 +13,7 @@
hide-bottom
>
<template v-slot:top>
<q-btn dense flat push @click="refreshServices" icon="refresh" />
<q-btn dense flat push @click="getServices" icon="refresh" />
<q-space />
<q-input v-model="filter" outlined label="Search" dense clearable>
<template v-slot:prepend>
@@ -242,7 +242,7 @@ export default {
.then(r => {
this.serviceDetailVisible = false;
this.serviceDetailsModal = false;
this.refreshServices();
this.getServices();
this.notifySuccess(`Service ${name} was edited!`);
})
.catch(e => {
@@ -303,7 +303,7 @@ export default {
this.$axios
.post("/services/serviceaction/", data)
.then(r => {
this.refreshServices();
this.getServices();
this.serviceDetailsModal = false;
this.notifySuccess(`Service ${fullname} was ${status}!`);
})
@@ -313,19 +313,9 @@ export default {
});
},
getServices() {
this.$q.loading.show({ message: "Loading services..." });
this.$axios
.get(`/services/${this.pk}/services/`)
.then(r => {
this.servicesData = [r.data][0].services;
})
.catch(e => {
this.notifyError(e.response.data);
});
},
refreshServices() {
this.$q.loading.show({ message: "Reloading services..." });
this.$axios
.get(`/services/${this.pk}/refreshedservices/`)
.then(r => {
this.servicesData = [r.data][0].services;
this.$q.loading.hide();

View File

@@ -1,6 +1,7 @@
<template>
<div v-if="Object.keys(summary).length === 0">No agent selected</div>
<div v-else>
<q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" />
<span>
<b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> &bull; <q-badge color="warning"> Maintenance Mode </q-badge> </span>
@@ -87,8 +88,12 @@
</template>
<script>
import { mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
export default {
name: "SummaryTab",
mixins: [mixins],
data() {
return {};
},
@@ -96,8 +101,22 @@ export default {
awaitingSync(total, passing, failing) {
return total !== 0 && passing === 0 && failing === 0 ? true : false;
},
refreshSummary() {
this.$q.loading.show();
this.$axios
.get(`/agents/${this.selectedAgentPk}/wmi/`)
.then(r => {
this.$store.dispatch("loadSummary", this.selectedAgentPk);
this.$q.loading.hide();
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data);
});
},
},
computed: {
...mapGetters(["selectedAgentPk"]),
summary() {
return this.$store.state.agentSummary;
},

View File

@@ -27,10 +27,13 @@
<p>Fix issues with the Tactical Checkrunner windows service which handles running all checks.</p>
</q-card-section>
<q-card-section v-show="mode === 'salt'">
<p>Fix issues with the salt-minion which handles windows updates, chocolatey and scheduled tasks.</p>
<p>Fix issues with the salt-minion which handles windows updates and chocolatey.</p>
</q-card-section>
<q-card-section v-show="mode === 'rpc'">
<p>Fix issues with the Tactical RPC service which handles most of the agent's realtime functions.</p>
<p>
Fix issues with the Tactical RPC service which handles most of the agent's realtime functions and scheduled
tasks.
</p>
</q-card-section>
<q-card-section v-show="mode === 'command'">
<p>Run a shell command on the agent.</p>