Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e413c0264a | ||
|
|
f88e7f898c | ||
|
|
d07bd4a6db | ||
|
|
fb34c099d5 | ||
|
|
1d2ee56a15 | ||
|
|
86665f7f09 | ||
|
|
0d2b4af986 | ||
|
|
dc2b2eeb9f | ||
|
|
e5dbb66d53 | ||
|
|
3474b1c471 | ||
|
|
3886de5b7c | ||
|
|
2b3cec06b3 | ||
|
|
8536754d14 | ||
|
|
1f36235801 | ||
|
|
a4194b14f9 | ||
|
|
2dcc629d9d | ||
|
|
98ddadc6bc | ||
|
|
f6e47b7383 | ||
|
|
f073ddc906 | ||
|
|
3e00631925 | ||
|
|
9b7ac58562 | ||
|
|
f242ddd801 | ||
|
|
c129886fe2 | ||
|
|
f577e814cf | ||
|
|
c860a0cedd | ||
|
|
ae7e28e492 | ||
|
|
90a63234ad | ||
|
|
14bca52e8f | ||
|
|
2f3c3361cf | ||
|
|
4034134055 | ||
|
|
c04f94cb7b | ||
|
|
fd1bbc7925 | ||
|
|
ff69bed394 | ||
|
|
d6e8c5146f | ||
|
|
9a04cf99d7 | ||
|
|
86e7c11e71 |
12
.github/workflows/docker-build-push.yml
vendored
12
.github/workflows/docker-build-push.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical/dockerfile
|
file: ./docker/containers/tactical/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
- name: Build and Push Tactical MeshCentral Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical-meshcentral/dockerfile
|
file: ./docker/containers/tactical-meshcentral/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
- name: Build and Push Tactical NATS Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical-nats/dockerfile
|
file: ./docker/containers/tactical-nats/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
- name: Build and Push Tactical Salt Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical-salt/dockerfile
|
file: ./docker/containers/tactical-salt/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
- name: Build and Push Tactical Frontend Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical-frontend/dockerfile
|
file: ./docker/containers/tactical-frontend/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
- name: Build and Push Tactical Nginx Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
@@ -85,4 +85,4 @@ jobs:
|
|||||||
pull: true
|
pull: true
|
||||||
file: ./docker/containers/tactical-nginx/dockerfile
|
file: ./docker/containers/tactical-nginx/dockerfile
|
||||||
platforms: linux/amd64
|
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
|
||||||
|
|||||||
@@ -20,6 +20,5 @@ omit =
|
|||||||
*/urls.py
|
*/urls.py
|
||||||
*/tests.py
|
*/tests.py
|
||||||
*/test.py
|
*/test.py
|
||||||
api/*.py
|
|
||||||
checks/utils.py
|
checks/utils.py
|
||||||
|
|
||||||
|
|||||||
@@ -164,13 +164,11 @@ class Agent(BaseAuditModel):
|
|||||||
elif i.status == "failing":
|
elif i.status == "failing":
|
||||||
failing += 1
|
failing += 1
|
||||||
|
|
||||||
has_failing_checks = True if failing > 0 else False
|
|
||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
"total": total,
|
"total": total,
|
||||||
"passing": passing,
|
"passing": passing,
|
||||||
"failing": failing,
|
"failing": failing,
|
||||||
"has_failing_checks": has_failing_checks,
|
"has_failing_checks": failing > 0,
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -546,6 +544,7 @@ class Agent(BaseAuditModel):
|
|||||||
|
|
||||||
ret = AgentEditSerializer(agent).data
|
ret = AgentEditSerializer(agent).data
|
||||||
del ret["all_timezones"]
|
del ret["all_timezones"]
|
||||||
|
del ret["client"]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import asyncio
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import random
|
import random
|
||||||
import requests
|
import requests
|
||||||
from packaging import version as pyver
|
from packaging import version as pyver
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
@@ -13,137 +15,104 @@ from logs.models import PendingAction
|
|||||||
|
|
||||||
logger.configure(**settings.LOG_CONFIG)
|
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"
|
||||||
|
|
||||||
|
# force an update to 1.1.5 since 1.1.6 needs agent to be on 1.1.5 first
|
||||||
|
if pyver.parse(agent.version) < pyver.parse("1.1.5"):
|
||||||
|
version = "1.1.5"
|
||||||
|
if agent.arch == "64":
|
||||||
|
url = "https://github.com/wh1te909/rmmagent/releases/download/v1.1.5/winagent-v1.1.5.exe"
|
||||||
|
inno = "winagent-v1.1.5.exe"
|
||||||
|
elif agent.arch == "32":
|
||||||
|
url = "https://github.com/wh1te909/rmmagent/releases/download/v1.1.5/winagent-v1.1.5-x86.exe"
|
||||||
|
inno = "winagent-v1.1.5-x86.exe"
|
||||||
|
else:
|
||||||
|
return "nover"
|
||||||
|
else:
|
||||||
|
version = settings.LATEST_AGENT_VER
|
||||||
|
url = agent.winagent_dl
|
||||||
|
inno = agent.win_inno_exe
|
||||||
|
|
||||||
|
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(version):
|
||||||
|
action.delete()
|
||||||
|
else:
|
||||||
|
return "pending"
|
||||||
|
|
||||||
|
PendingAction.objects.create(
|
||||||
|
agent=agent,
|
||||||
|
action_type="agentupdate",
|
||||||
|
details={
|
||||||
|
"url": url,
|
||||||
|
"version": version,
|
||||||
|
"inno": inno,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
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": inno,
|
||||||
|
"url": url,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return "salt"
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def send_agent_update_task(pks, version):
|
def send_agent_update_task(pks: List[int], version: str) -> None:
|
||||||
assert isinstance(pks, list)
|
|
||||||
|
|
||||||
q = Agent.objects.filter(pk__in=pks)
|
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 pk in agents:
|
||||||
|
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
|
@app.task
|
||||||
def auto_self_agent_update_task():
|
def auto_self_agent_update_task() -> None:
|
||||||
core = CoreSettings.objects.first()
|
core = CoreSettings.objects.first()
|
||||||
if not core.agent_auto_update:
|
if not core.agent_auto_update:
|
||||||
logger.info("Agent auto update is disabled. Skipping.")
|
logger.info("Agent auto update is disabled. Skipping.")
|
||||||
return
|
return
|
||||||
|
|
||||||
q = Agent.objects.only("pk", "version")
|
q = Agent.objects.only("pk", "version")
|
||||||
agents = [
|
pks: List[int] = [
|
||||||
i.pk
|
i.pk
|
||||||
for i in q
|
for i in q
|
||||||
if pyver.parse(i.version) < pyver.parse(settings.LATEST_AGENT_VER)
|
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
|
@app.task
|
||||||
if agent.arch is None:
|
def sync_sysinfo_task():
|
||||||
logger.warning(
|
agents = Agent.objects.all()
|
||||||
f"Unable to determine arch on {agent.salt_id}. Skipping."
|
online = [
|
||||||
)
|
i
|
||||||
continue
|
for i in agents
|
||||||
|
if pyver.parse(i.version) >= pyver.parse("1.1.3") and i.status == "online"
|
||||||
# golang agent only backwards compatible with py agent 0.11.2
|
]
|
||||||
# force an upgrade to the latest python agent if version < 0.11.2
|
for agent in online:
|
||||||
if pyver.parse(agent.version) < pyver.parse("0.11.2"):
|
asyncio.run(agent.nats_cmd({"func": "sync"}, wait=False))
|
||||||
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
|
@app.task
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ from unittest.mock import patch
|
|||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
|
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone as djangotime
|
from django.utils import timezone as djangotime
|
||||||
|
from logs.models import PendingAction
|
||||||
|
|
||||||
from tacticalrmm.test import TacticalTestCase
|
from tacticalrmm.test import TacticalTestCase
|
||||||
from .serializers import AgentSerializer
|
from .serializers import AgentSerializer
|
||||||
from winupdate.serializers import WinUpdatePolicySerializer
|
from winupdate.serializers import WinUpdatePolicySerializer
|
||||||
from .models import Agent
|
from .models import Agent
|
||||||
from .tasks import (
|
from .tasks import (
|
||||||
|
agent_recovery_sms_task,
|
||||||
auto_self_agent_update_task,
|
auto_self_agent_update_task,
|
||||||
sync_salt_modules_task,
|
sync_salt_modules_task,
|
||||||
batch_sync_modules_task,
|
batch_sync_modules_task,
|
||||||
OLD_64_PY_AGENT,
|
|
||||||
OLD_32_PY_AGENT,
|
|
||||||
)
|
)
|
||||||
from winupdate.models import WinUpdatePolicy
|
from winupdate.models import WinUpdatePolicy
|
||||||
|
|
||||||
@@ -786,6 +787,70 @@ class TestAgentTasks(TacticalTestCase):
|
|||||||
self.assertEqual(ret.status, "SUCCESS")
|
self.assertEqual(ret.status, "SUCCESS")
|
||||||
|
|
||||||
@patch("agents.models.Agent.salt_api_async")
|
@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)
|
@patch("agents.tasks.sleep", return_value=None)
|
||||||
def test_auto_self_agent_update_task(self, mock_sleep, salt_api_async):
|
def test_auto_self_agent_update_task(self, mock_sleep, salt_api_async):
|
||||||
# test 64bit golang agent
|
# test 64bit golang agent
|
||||||
@@ -888,4 +953,4 @@ class TestAgentTasks(TacticalTestCase):
|
|||||||
"url": OLD_32_PY_AGENT,
|
"url": OLD_32_PY_AGENT,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(ret.status, "SUCCESS")
|
self.assertEqual(ret.status, "SUCCESS") """
|
||||||
@@ -30,4 +30,5 @@ urlpatterns = [
|
|||||||
path("bulk/", views.bulk),
|
path("bulk/", views.bulk),
|
||||||
path("agent_counts/", views.agent_counts),
|
path("agent_counts/", views.agent_counts),
|
||||||
path("maintenance/", views.agent_maintenance),
|
path("maintenance/", views.agent_maintenance),
|
||||||
|
path("<int:pk>/wmi/", views.WMI.as_view()),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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))
|
r = asyncio.run(agent.nats_cmd(nats_data, timeout=10))
|
||||||
if r != "ok":
|
if r != "ok":
|
||||||
return notify_error(r)
|
return notify_error(r)
|
||||||
@@ -895,3 +898,15 @@ def agent_maintenance(request):
|
|||||||
return notify_error("Invalid data")
|
return notify_error("Invalid data")
|
||||||
|
|
||||||
return Response("ok")
|
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")
|
||||||
@@ -5,6 +5,7 @@ from tacticalrmm.celery import app
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import pytz
|
import pytz
|
||||||
from django.utils import timezone as djangotime
|
from django.utils import timezone as djangotime
|
||||||
|
from packaging import version as pyver
|
||||||
|
|
||||||
from .models import AutomatedTask
|
from .models import AutomatedTask
|
||||||
from logs.models import PendingAction
|
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":
|
elif task.task_type == "checkfailure" or task.task_type == "manual":
|
||||||
nats_data = {
|
nats_data = {
|
||||||
"func": "schedtask",
|
"func": "schedtask",
|
||||||
@@ -201,6 +207,7 @@ def remove_orphaned_win_tasks(agentpk):
|
|||||||
"TacticalRMM_fixmesh",
|
"TacticalRMM_fixmesh",
|
||||||
"TacticalRMM_SchedReboot",
|
"TacticalRMM_SchedReboot",
|
||||||
"TacticalRMM_sync",
|
"TacticalRMM_sync",
|
||||||
|
"TacticalRMM_agentupdate",
|
||||||
)
|
)
|
||||||
|
|
||||||
for task in r:
|
for task in r:
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class Client(BaseAuditModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def has_failing_checks(self):
|
def has_failing_checks(self):
|
||||||
|
|
||||||
agents = (
|
agents = (
|
||||||
Agent.objects.only(
|
Agent.objects.only(
|
||||||
"pk",
|
"pk",
|
||||||
@@ -50,14 +49,17 @@ class Client(BaseAuditModel):
|
|||||||
.filter(site__client=self)
|
.filter(site__client=self)
|
||||||
.prefetch_related("agentchecks")
|
.prefetch_related("agentchecks")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
failing = 0
|
||||||
for agent in agents:
|
for agent in agents:
|
||||||
if agent.checks["has_failing_checks"]:
|
if agent.checks["has_failing_checks"]:
|
||||||
return True
|
failing += 1
|
||||||
|
|
||||||
if agent.overdue_email_alert or agent.overdue_text_alert:
|
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
|
@staticmethod
|
||||||
def serialize(client):
|
def serialize(client):
|
||||||
@@ -98,7 +100,6 @@ class Site(BaseAuditModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def has_failing_checks(self):
|
def has_failing_checks(self):
|
||||||
|
|
||||||
agents = (
|
agents = (
|
||||||
Agent.objects.only(
|
Agent.objects.only(
|
||||||
"pk",
|
"pk",
|
||||||
@@ -110,14 +111,17 @@ class Site(BaseAuditModel):
|
|||||||
.filter(site=self)
|
.filter(site=self)
|
||||||
.prefetch_related("agentchecks")
|
.prefetch_related("agentchecks")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
failing = 0
|
||||||
for agent in agents:
|
for agent in agents:
|
||||||
if agent.checks["has_failing_checks"]:
|
if agent.checks["has_failing_checks"]:
|
||||||
return True
|
failing += 1
|
||||||
|
|
||||||
if agent.overdue_email_alert or agent.overdue_text_alert:
|
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
|
@staticmethod
|
||||||
def serialize(site):
|
def serialize(site):
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func main() {
|
|||||||
debugLog := flag.String("log", "", "Verbose output")
|
debugLog := flag.String("log", "", "Verbose output")
|
||||||
localMesh := flag.String("local-mesh", "", "Use local mesh agent")
|
localMesh := flag.String("local-mesh", "", "Use local mesh agent")
|
||||||
noSalt := flag.Bool("nosalt", false, "Does not install salt")
|
noSalt := flag.Bool("nosalt", false, "Does not install salt")
|
||||||
|
silent := flag.Bool("silent", false, "Do not popup any message boxes during installation")
|
||||||
cert := flag.String("cert", "", "Path to ca.pem")
|
cert := flag.String("cert", "", "Path to ca.pem")
|
||||||
timeout := flag.String("timeout", "", "Timeout for subprocess calls")
|
timeout := flag.String("timeout", "", "Timeout for subprocess calls")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -78,7 +79,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
cmdArgs = append(cmdArgs, "--log", "DEBUG")
|
cmdArgs = append(cmdArgs, "-log", "debug")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *silent {
|
||||||
|
cmdArgs = append(cmdArgs, "-silent")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *noSalt {
|
if *noSalt {
|
||||||
@@ -86,27 +91,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(strings.TrimSpace(*localMesh)) != 0 {
|
if len(strings.TrimSpace(*localMesh)) != 0 {
|
||||||
cmdArgs = append(cmdArgs, "--local-mesh", *localMesh)
|
cmdArgs = append(cmdArgs, "-local-mesh", *localMesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(strings.TrimSpace(*cert)) != 0 {
|
if len(strings.TrimSpace(*cert)) != 0 {
|
||||||
cmdArgs = append(cmdArgs, "--cert", *cert)
|
cmdArgs = append(cmdArgs, "-cert", *cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(strings.TrimSpace(*timeout)) != 0 {
|
if len(strings.TrimSpace(*timeout)) != 0 {
|
||||||
cmdArgs = append(cmdArgs, "--timeout", *timeout)
|
cmdArgs = append(cmdArgs, "-timeout", *timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if Rdp == "1" {
|
if Rdp == "1" {
|
||||||
cmdArgs = append(cmdArgs, "--rdp")
|
cmdArgs = append(cmdArgs, "-rdp")
|
||||||
}
|
}
|
||||||
|
|
||||||
if Ping == "1" {
|
if Ping == "1" {
|
||||||
cmdArgs = append(cmdArgs, "--ping")
|
cmdArgs = append(cmdArgs, "-ping")
|
||||||
}
|
}
|
||||||
|
|
||||||
if Power == "1" {
|
if Power == "1" {
|
||||||
cmdArgs = append(cmdArgs, "--power")
|
cmdArgs = append(cmdArgs, "-power")
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ def agent_pending_actions(request, pk):
|
|||||||
|
|
||||||
@api_view()
|
@api_view()
|
||||||
def all_pending_actions(request):
|
def all_pending_actions(request):
|
||||||
actions = PendingAction.objects.all()
|
actions = PendingAction.objects.all().select_related("agent")
|
||||||
return Response(PendingActionSerializer(actions, many=True).data)
|
return Response(PendingActionSerializer(actions, many=True).data)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
amqp==2.6.1
|
amqp==2.6.1
|
||||||
asgiref==3.3.0
|
asgiref==3.3.1
|
||||||
asyncio-nats-client==0.11.4
|
asyncio-nats-client==0.11.4
|
||||||
billiard==3.6.3.0
|
billiard==3.6.3.0
|
||||||
celery==4.4.6
|
celery==4.4.6
|
||||||
certifi==2020.11.8
|
certifi==2020.12.5
|
||||||
cffi==1.14.3
|
cffi==1.14.3
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
cryptography==3.2.1
|
cryptography==3.2.1
|
||||||
decorator==4.4.2
|
decorator==4.4.2
|
||||||
Django==3.1.3
|
Django==3.1.4
|
||||||
django-cors-headers==3.5.0
|
django-cors-headers==3.5.0
|
||||||
django-rest-knox==4.1.0
|
django-rest-knox==4.1.0
|
||||||
djangorestframework==3.12.2
|
djangorestframework==3.12.2
|
||||||
@@ -26,12 +26,11 @@ pyparsing==2.4.7
|
|||||||
pytz==2020.4
|
pytz==2020.4
|
||||||
qrcode==6.1
|
qrcode==6.1
|
||||||
redis==3.5.3
|
redis==3.5.3
|
||||||
requests==2.24.0
|
requests==2.25.0
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
sqlparse==0.4.1
|
sqlparse==0.4.1
|
||||||
tldextract==3.0.2
|
twilio==6.49.0
|
||||||
twilio==6.47.0
|
urllib3==1.26.2
|
||||||
urllib3==1.25.11
|
|
||||||
uWSGI==2.0.19.1
|
uWSGI==2.0.19.1
|
||||||
validators==0.18.1
|
validators==0.18.1
|
||||||
vine==1.3.0
|
vine==1.3.0
|
||||||
|
|||||||
@@ -9,21 +9,6 @@ class TestServiceViews(TacticalTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.authenticate()
|
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):
|
def test_default_services(self):
|
||||||
url = "/services/defaultservices/"
|
url = "/services/defaultservices/"
|
||||||
resp = self.client.get(url, format="json")
|
resp = self.client.get(url, format="json")
|
||||||
@@ -33,13 +18,13 @@ class TestServiceViews(TacticalTestCase):
|
|||||||
self.check_not_authenticated("get", url)
|
self.check_not_authenticated("get", url)
|
||||||
|
|
||||||
@patch("agents.models.Agent.nats_cmd")
|
@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
|
# 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)
|
self.assertEqual(resp.status_code, 404)
|
||||||
|
|
||||||
agent = baker.make_recipe("agents.agent_with_services")
|
agent = baker.make_recipe("agents.agent_with_services")
|
||||||
url = f"/services/{agent.pk}/refreshedservices/"
|
url = f"/services/{agent.pk}/services/"
|
||||||
|
|
||||||
nats_return = [
|
nats_return = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from . import views
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("<int:pk>/services/", views.get_services),
|
path("<int:pk>/services/", views.get_services),
|
||||||
path("defaultservices/", views.default_services),
|
path("defaultservices/", views.default_services),
|
||||||
path("<int:pk>/refreshedservices/", views.get_refreshed_services),
|
|
||||||
path("serviceaction/", views.service_action),
|
path("serviceaction/", views.service_action),
|
||||||
path("<int:pk>/<svcname>/servicedetail/", views.service_detail),
|
path("<int:pk>/<svcname>/servicedetail/", views.service_detail),
|
||||||
path("editservice/", views.edit_service),
|
path("editservice/", views.edit_service),
|
||||||
|
|||||||
@@ -19,17 +19,6 @@ logger.configure(**settings.LOG_CONFIG)
|
|||||||
|
|
||||||
@api_view()
|
@api_view()
|
||||||
def get_services(request, pk):
|
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)
|
agent = get_object_or_404(Agent, pk=pk)
|
||||||
if not agent.has_nats:
|
if not agent.has_nats:
|
||||||
return notify_error("Requires agent version 1.1.0 or greater")
|
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)
|
return Response(ServicesSerializer(agent).data)
|
||||||
|
|
||||||
|
|
||||||
|
@api_view()
|
||||||
|
def default_services(request):
|
||||||
|
return Response(Check.load_default_services())
|
||||||
|
|
||||||
|
|
||||||
@api_view(["POST"])
|
@api_view(["POST"])
|
||||||
def service_action(request):
|
def service_action(request):
|
||||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ app.conf.beat_schedule = {
|
|||||||
"task": "agents.tasks.auto_self_agent_update_task",
|
"task": "agents.tasks.auto_self_agent_update_task",
|
||||||
"schedule": crontab(minute=35, hour="*"),
|
"schedule": crontab(minute=35, hour="*"),
|
||||||
},
|
},
|
||||||
|
"agents-sync": {
|
||||||
|
"task": "agents.tasks.sync_sysinfo_task",
|
||||||
|
"schedule": crontab(minute=55, hour="*"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,15 @@ def get_debug_info():
|
|||||||
EXCLUDE_PATHS = (
|
EXCLUDE_PATHS = (
|
||||||
"/api/v3",
|
"/api/v3",
|
||||||
"/api/v2",
|
"/api/v2",
|
||||||
"/api/v1",
|
|
||||||
"/logs/auditlogs",
|
"/logs/auditlogs",
|
||||||
"/winupdate/winupdater",
|
|
||||||
"/winupdate/results",
|
|
||||||
f"/{settings.ADMIN_URL}",
|
f"/{settings.ADMIN_URL}",
|
||||||
"/logout",
|
"/logout",
|
||||||
"/agents/installagent",
|
"/agents/installagent",
|
||||||
"/logs/downloadlog",
|
"/logs/downloadlog",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ENDS_WITH = "/services/"
|
||||||
|
|
||||||
|
|
||||||
class AuditMiddleware:
|
class AuditMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
@@ -37,7 +36,9 @@ class AuditMiddleware:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
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
|
# https://stackoverflow.com/questions/26240832/django-and-middleware-which-uses-request-user-is-always-anonymous
|
||||||
try:
|
try:
|
||||||
# DRF saves the class of the view function as the .cls property
|
# DRF saves the class of the view function as the .cls property
|
||||||
|
|||||||
@@ -15,25 +15,25 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
|
|||||||
AUTH_USER_MODEL = "accounts.User"
|
AUTH_USER_MODEL = "accounts.User"
|
||||||
|
|
||||||
# latest release
|
# latest release
|
||||||
TRMM_VERSION = "0.2.4"
|
TRMM_VERSION = "0.2.12"
|
||||||
|
|
||||||
# bump this version everytime vue code is changed
|
# bump this version everytime vue code is changed
|
||||||
# to alert user they need to manually refresh their browser
|
# to alert user they need to manually refresh their browser
|
||||||
APP_VER = "0.0.94"
|
APP_VER = "0.0.99"
|
||||||
|
|
||||||
# https://github.com/wh1te909/salt
|
# https://github.com/wh1te909/salt
|
||||||
LATEST_SALT_VER = "1.1.0"
|
LATEST_SALT_VER = "1.1.0"
|
||||||
|
|
||||||
# https://github.com/wh1te909/rmmagent
|
# https://github.com/wh1te909/rmmagent
|
||||||
LATEST_AGENT_VER = "1.1.1"
|
LATEST_AGENT_VER = "1.1.7"
|
||||||
|
|
||||||
MESH_VER = "0.7.9"
|
MESH_VER = "0.7.14"
|
||||||
|
|
||||||
SALT_MASTER_VER = "3002.2"
|
SALT_MASTER_VER = "3002.2"
|
||||||
|
|
||||||
# for the update script, bump when need to recreate venv or npm install
|
# for the update script, bump when need to recreate venv or npm install
|
||||||
PIP_VER = "3"
|
PIP_VER = "4"
|
||||||
NPM_VER = "2"
|
NPM_VER = "3"
|
||||||
|
|
||||||
DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe"
|
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"
|
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -117,6 +118,7 @@ def reload_nats():
|
|||||||
json.dump(config, f)
|
json.dump(config, f)
|
||||||
|
|
||||||
if not settings.DOCKER_BUILD:
|
if not settings.DOCKER_BUILD:
|
||||||
|
time.sleep(0.5)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["/usr/local/bin/nats-server", "-signal", "reload"], capture_output=True
|
["/usr/local/bin/nats-server", "-signal", "reload"], capture_output=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ def check_agent_update_schedule_task():
|
|||||||
def check_for_updates_task(pk, wait=False, auto_approve=False):
|
def check_for_updates_task(pk, wait=False, auto_approve=False):
|
||||||
|
|
||||||
if wait:
|
if wait:
|
||||||
sleep(70)
|
sleep(120)
|
||||||
|
|
||||||
agent = Agent.objects.get(pk=pk)
|
agent = Agent.objects.get(pk=pk)
|
||||||
ret = agent.salt_api_cmd(
|
ret = agent.salt_api_cmd(
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from . import views
|
from . import views
|
||||||
from apiv3 import views as v3_views
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("<int:pk>/getwinupdates/", views.get_win_updates),
|
path("<int:pk>/getwinupdates/", views.get_win_updates),
|
||||||
path("<int:pk>/runupdatescan/", views.run_update_scan),
|
path("<int:pk>/runupdatescan/", views.run_update_scan),
|
||||||
path("editpolicy/", views.edit_policy),
|
path("editpolicy/", views.edit_policy),
|
||||||
path("winupdater/", views.win_updater),
|
|
||||||
path("results/", v3_views.WinUpdater.as_view()),
|
|
||||||
path("<int:pk>/installnow/", views.install_updates),
|
path("<int:pk>/installnow/", views.install_updates),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -58,20 +58,3 @@ def edit_policy(request):
|
|||||||
patch.action = request.data["policy"]
|
patch.action = request.data["policy"]
|
||||||
patch.save(update_fields=["action"])
|
patch.save(update_fields=["action"])
|
||||||
return Response("ok")
|
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")
|
|
||||||
@@ -27,15 +27,21 @@ jobs:
|
|||||||
source env/bin/activate
|
source env/bin/activate
|
||||||
cd /myagent/_work/1/s/api/tacticalrmm
|
cd /myagent/_work/1/s/api/tacticalrmm
|
||||||
pip install --no-cache-dir --upgrade pip
|
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
|
pip install --no-cache-dir -r requirements.txt -r requirements-test.txt
|
||||||
displayName: "Install Python Dependencies"
|
displayName: "Install Python Dependencies"
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
cd /myagent/_work/1/s/api
|
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
|
source env/bin/activate
|
||||||
cd /myagent/_work/1/s/api/tacticalrmm
|
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"
|
displayName: "Run django tests"
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
SCRIPT_VERSION="4"
|
SCRIPT_VERSION="5"
|
||||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
|
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
|
||||||
|
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -50,6 +50,11 @@ if [ -d /meshcentral/meshcentral-coredumps ]; then
|
|||||||
rm -f /meshcentral/meshcentral-coredumps/*
|
rm -f /meshcentral/meshcentral-coredumps/*
|
||||||
fi
|
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')
|
dt_now=$(date '+%Y_%m_%d__%H_%M_%S')
|
||||||
tmp_dir=$(mktemp -d -t tacticalrmm-XXXXXXXXXXXXXXXXXXXXX)
|
tmp_dir=$(mktemp -d -t tacticalrmm-XXXXXXXXXXXXXXXXXXXXX)
|
||||||
sysd="/etc/systemd/system"
|
sysd="/etc/systemd/system"
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ DEBUG = False
|
|||||||
DOCKER_BUILD = True
|
DOCKER_BUILD = True
|
||||||
|
|
||||||
CERT_FILE = '/opt/tactical/certs/fullchain.pem'
|
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'
|
SCRIPTS_DIR = '/opt/tactical/scripts'
|
||||||
|
|
||||||
|
|||||||
82
install.sh
82
install.sh
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
SCRIPT_VERSION="25"
|
SCRIPT_VERSION="27"
|
||||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
|
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
|
||||||
|
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -205,12 +205,47 @@ sudo apt install -y mongodb-org
|
|||||||
sudo systemctl enable mongod
|
sudo systemctl enable mongod
|
||||||
sudo systemctl restart mongod
|
sudo systemctl restart mongod
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print_green 'Installing python, redis and git'
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y python3.8-venv python3.8-dev python3-pip python3-cherrypy3 python3-setuptools python3-wheel ca-certificates redis git
|
||||||
|
|
||||||
|
print_green 'Installing postgresql'
|
||||||
|
|
||||||
|
sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||||
|
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y postgresql-13
|
||||||
|
|
||||||
|
print_green 'Creating database for the rmm'
|
||||||
|
|
||||||
|
sudo -u postgres psql -c "CREATE DATABASE tacticalrmm"
|
||||||
|
sudo -u postgres psql -c "CREATE USER ${pgusername} WITH PASSWORD '${pgpw}'"
|
||||||
|
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET client_encoding TO 'utf8'"
|
||||||
|
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET default_transaction_isolation TO 'read committed'"
|
||||||
|
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET timezone TO 'UTC'"
|
||||||
|
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE tacticalrmm TO ${pgusername}"
|
||||||
|
|
||||||
|
sudo mkdir /rmm
|
||||||
|
sudo chown ${USER}:${USER} /rmm
|
||||||
|
sudo mkdir -p /var/log/celery
|
||||||
|
sudo chown ${USER}:${USER} /var/log/celery
|
||||||
|
git clone https://github.com/wh1te909/tacticalrmm.git /rmm/
|
||||||
|
cd /rmm
|
||||||
|
git config user.email "admin@example.com"
|
||||||
|
git config user.name "Bob"
|
||||||
|
git checkout master
|
||||||
|
|
||||||
print_green 'Installing MeshCentral'
|
print_green 'Installing MeshCentral'
|
||||||
|
|
||||||
|
MESH_VER=$(grep "^MESH_VER" /rmm/api/tacticalrmm/tacticalrmm/settings.py | awk -F'[= "]' '{print $5}')
|
||||||
|
|
||||||
sudo mkdir -p /meshcentral/meshcentral-data
|
sudo mkdir -p /meshcentral/meshcentral-data
|
||||||
sudo chown ${USER}:${USER} -R /meshcentral
|
sudo chown ${USER}:${USER} -R /meshcentral
|
||||||
cd /meshcentral
|
cd /meshcentral
|
||||||
npm install meshcentral@0.7.9
|
npm install meshcentral@${MESH_VER}
|
||||||
sudo chown ${USER}:${USER} -R /meshcentral
|
sudo chown ${USER}:${USER} -R /meshcentral
|
||||||
|
|
||||||
meshcfg="$(cat << EOF
|
meshcfg="$(cat << EOF
|
||||||
@@ -253,37 +288,6 @@ EOF
|
|||||||
)"
|
)"
|
||||||
echo "${meshcfg}" > /meshcentral/meshcentral-data/config.json
|
echo "${meshcfg}" > /meshcentral/meshcentral-data/config.json
|
||||||
|
|
||||||
print_green 'Installing python, redis and git'
|
|
||||||
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y python3.8-venv python3.8-dev python3-pip python3-cherrypy3 python3-setuptools python3-wheel ca-certificates redis git
|
|
||||||
|
|
||||||
print_green 'Installing postgresql'
|
|
||||||
|
|
||||||
sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
|
||||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y postgresql-13
|
|
||||||
|
|
||||||
print_green 'Creating database for the rmm'
|
|
||||||
|
|
||||||
sudo -u postgres psql -c "CREATE DATABASE tacticalrmm"
|
|
||||||
sudo -u postgres psql -c "CREATE USER ${pgusername} WITH PASSWORD '${pgpw}'"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET client_encoding TO 'utf8'"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET default_transaction_isolation TO 'read committed'"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE ${pgusername} SET timezone TO 'UTC'"
|
|
||||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE tacticalrmm TO ${pgusername}"
|
|
||||||
|
|
||||||
sudo mkdir /rmm
|
|
||||||
sudo chown ${USER}:${USER} /rmm
|
|
||||||
sudo mkdir -p /var/log/celery
|
|
||||||
sudo chown ${USER}:${USER} /var/log/celery
|
|
||||||
git clone https://github.com/wh1te909/tacticalrmm.git /rmm/
|
|
||||||
cd /rmm
|
|
||||||
git config user.email "admin@example.com"
|
|
||||||
git config user.name "Bob"
|
|
||||||
git checkout master
|
|
||||||
|
|
||||||
localvars="$(cat << EOF
|
localvars="$(cat << EOF
|
||||||
SECRET_KEY = "${DJANGO_SEKRET}"
|
SECRET_KEY = "${DJANGO_SEKRET}"
|
||||||
|
|
||||||
@@ -348,7 +352,7 @@ python3 -m venv env
|
|||||||
source /rmm/api/env/bin/activate
|
source /rmm/api/env/bin/activate
|
||||||
cd /rmm/api/tacticalrmm
|
cd /rmm/api/tacticalrmm
|
||||||
pip install --no-cache-dir --upgrade pip
|
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
|
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py collectstatic --no-input
|
python manage.py collectstatic --no-input
|
||||||
@@ -504,14 +508,7 @@ nginxmesh="$(cat << EOF
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name ${meshdomain};
|
server_name ${meshdomain};
|
||||||
location / {
|
return 301 https://\$server_name\$request_uri;
|
||||||
proxy_pass http://127.0.0.1:800;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header X-Forwarded-Host \$host:\$server_port;
|
|
||||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -530,6 +527,7 @@ server {
|
|||||||
proxy_pass http://127.0.0.1:4430/;
|
proxy_pass http://127.0.0.1:4430/;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_set_header Host \$host;
|
||||||
proxy_set_header Upgrade \$http_upgrade;
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
proxy_set_header X-Forwarded-Host \$host:\$server_port;
|
proxy_set_header X-Forwarded-Host \$host:\$server_port;
|
||||||
|
|||||||
24
restore.sh
24
restore.sh
@@ -7,7 +7,7 @@ pgpw="hunter2"
|
|||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
SCRIPT_VERSION="8"
|
SCRIPT_VERSION="10"
|
||||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
|
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
|
||||||
|
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@@ -207,15 +207,6 @@ sudo systemctl restart mongod
|
|||||||
sleep 5
|
sleep 5
|
||||||
mongorestore --gzip $tmp_dir/meshcentral/mongo
|
mongorestore --gzip $tmp_dir/meshcentral/mongo
|
||||||
|
|
||||||
print_green 'Restoring MeshCentral'
|
|
||||||
|
|
||||||
sudo tar -xzf $tmp_dir/meshcentral/mesh.tar.gz -C /
|
|
||||||
sudo chown ${USER}:${USER} -R /meshcentral
|
|
||||||
cd /meshcentral
|
|
||||||
npm install meshcentral@0.7.9
|
|
||||||
|
|
||||||
|
|
||||||
print_green 'Restoring the backend'
|
|
||||||
|
|
||||||
sudo mkdir /rmm
|
sudo mkdir /rmm
|
||||||
sudo chown ${USER}:${USER} /rmm
|
sudo chown ${USER}:${USER} /rmm
|
||||||
@@ -227,6 +218,17 @@ git config user.email "admin@example.com"
|
|||||||
git config user.name "Bob"
|
git config user.name "Bob"
|
||||||
git checkout master
|
git checkout master
|
||||||
|
|
||||||
|
print_green 'Restoring MeshCentral'
|
||||||
|
|
||||||
|
MESH_VER=$(grep "^MESH_VER" /rmm/api/tacticalrmm/tacticalrmm/settings.py | awk -F'[= "]' '{print $5}')
|
||||||
|
sudo tar -xzf $tmp_dir/meshcentral/mesh.tar.gz -C /
|
||||||
|
sudo chown ${USER}:${USER} -R /meshcentral
|
||||||
|
cd /meshcentral
|
||||||
|
npm install meshcentral@${MESH_VER}
|
||||||
|
|
||||||
|
|
||||||
|
print_green 'Restoring the backend'
|
||||||
|
|
||||||
cp $tmp_dir/rmm/local_settings.py /rmm/api/tacticalrmm/tacticalrmm/
|
cp $tmp_dir/rmm/local_settings.py /rmm/api/tacticalrmm/tacticalrmm/
|
||||||
cp $tmp_dir/rmm/env /rmm/web/.env
|
cp $tmp_dir/rmm/env /rmm/web/.env
|
||||||
cp $tmp_dir/rmm/app.ini /rmm/api/tacticalrmm/
|
cp $tmp_dir/rmm/app.ini /rmm/api/tacticalrmm/
|
||||||
@@ -244,7 +246,7 @@ python3 -m venv env
|
|||||||
source /rmm/api/env/bin/activate
|
source /rmm/api/env/bin/activate
|
||||||
cd /rmm/api/tacticalrmm
|
cd /rmm/api/tacticalrmm
|
||||||
pip install --no-cache-dir --upgrade pip
|
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
|
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
|
||||||
python manage.py collectstatic --no-input
|
python manage.py collectstatic --no-input
|
||||||
python manage.py reload_nats
|
python manage.py reload_nats
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
SCRIPT_VERSION="98"
|
SCRIPT_VERSION="100"
|
||||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh'
|
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'
|
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
|
||||||
YELLOW='\033[1;33m'
|
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 chown ${USER}:${USER} /usr/local/bin/goversioninfo
|
||||||
sudo chmod +x /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
|
if [[ "${CURRENT_PIP_VER}" != "${LATEST_PIP_VER}" ]]; then
|
||||||
rm -rf /rmm/api/env
|
rm -rf /rmm/api/env
|
||||||
cd /rmm/api
|
cd /rmm/api
|
||||||
@@ -184,7 +189,7 @@ if [[ "${CURRENT_PIP_VER}" != "${LATEST_PIP_VER}" ]]; then
|
|||||||
source /rmm/api/env/bin/activate
|
source /rmm/api/env/bin/activate
|
||||||
cd /rmm/api/tacticalrmm
|
cd /rmm/api/tacticalrmm
|
||||||
pip install --no-cache-dir --upgrade pip
|
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
|
pip install --no-cache-dir -r requirements.txt
|
||||||
else
|
else
|
||||||
source /rmm/api/env/bin/activate
|
source /rmm/api/env/bin/activate
|
||||||
|
|||||||
856
web/package-lock.json
generated
856
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,10 +13,10 @@
|
|||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"qrcode.vue": "^1.7.0",
|
"qrcode.vue": "^1.7.0",
|
||||||
"quasar": "^1.14.5"
|
"quasar": "^1.14.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@quasar/app": "^2.1.8",
|
"@quasar/app": "^2.1.9",
|
||||||
"@quasar/cli": "^1.1.2",
|
"@quasar/cli": "^1.1.2",
|
||||||
"@quasar/quasar-app-extension-testing": "^1.0.0",
|
"@quasar/quasar-app-extension-testing": "^1.0.0",
|
||||||
"@quasar/quasar-app-extension-testing-unit-jest": "^1.0.1",
|
"@quasar/quasar-app-extension-testing-unit-jest": "^1.0.1",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
hide-bottom
|
hide-bottom
|
||||||
>
|
>
|
||||||
<template v-slot:top>
|
<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-space />
|
||||||
<q-input v-model="filter" outlined label="Search" dense clearable>
|
<q-input v-model="filter" outlined label="Search" dense clearable>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
@@ -242,7 +242,7 @@ export default {
|
|||||||
.then(r => {
|
.then(r => {
|
||||||
this.serviceDetailVisible = false;
|
this.serviceDetailVisible = false;
|
||||||
this.serviceDetailsModal = false;
|
this.serviceDetailsModal = false;
|
||||||
this.refreshServices();
|
this.getServices();
|
||||||
this.notifySuccess(`Service ${name} was edited!`);
|
this.notifySuccess(`Service ${name} was edited!`);
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
@@ -303,7 +303,7 @@ export default {
|
|||||||
this.$axios
|
this.$axios
|
||||||
.post("/services/serviceaction/", data)
|
.post("/services/serviceaction/", data)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
this.refreshServices();
|
this.getServices();
|
||||||
this.serviceDetailsModal = false;
|
this.serviceDetailsModal = false;
|
||||||
this.notifySuccess(`Service ${fullname} was ${status}!`);
|
this.notifySuccess(`Service ${fullname} was ${status}!`);
|
||||||
})
|
})
|
||||||
@@ -313,19 +313,9 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
getServices() {
|
getServices() {
|
||||||
|
this.$q.loading.show({ message: "Loading services..." });
|
||||||
this.$axios
|
this.$axios
|
||||||
.get(`/services/${this.pk}/services/`)
|
.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 => {
|
.then(r => {
|
||||||
this.servicesData = [r.data][0].services;
|
this.servicesData = [r.data][0].services;
|
||||||
this.$q.loading.hide();
|
this.$q.loading.hide();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="Object.keys(summary).length === 0">No agent selected</div>
|
<div v-if="Object.keys(summary).length === 0">No agent selected</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
<q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" />
|
||||||
<span>
|
<span>
|
||||||
<b>{{ summary.hostname }}</b>
|
<b>{{ summary.hostname }}</b>
|
||||||
<span v-if="summary.maintenance_mode"> • <q-badge color="warning"> Maintenance Mode </q-badge> </span>
|
<span v-if="summary.maintenance_mode"> • <q-badge color="warning"> Maintenance Mode </q-badge> </span>
|
||||||
@@ -87,8 +88,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
import mixins from "@/mixins/mixins";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SummaryTab",
|
name: "SummaryTab",
|
||||||
|
mixins: [mixins],
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
@@ -96,8 +101,22 @@ export default {
|
|||||||
awaitingSync(total, passing, failing) {
|
awaitingSync(total, passing, failing) {
|
||||||
return total !== 0 && passing === 0 && failing === 0 ? true : false;
|
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: {
|
computed: {
|
||||||
|
...mapGetters(["selectedAgentPk"]),
|
||||||
summary() {
|
summary() {
|
||||||
return this.$store.state.agentSummary;
|
return this.$store.state.agentSummary;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,19 +26,25 @@
|
|||||||
>
|
>
|
||||||
<div class="q-pa-xs q-gutter-xs">
|
<div class="q-pa-xs q-gutter-xs">
|
||||||
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
||||||
<code>--log DEBUG</code>
|
<code>-log debug</code>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
<span>To enable verbose output during the install</span>
|
<span>To enable verbose output during the install</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="q-pa-xs q-gutter-xs">
|
<div class="q-pa-xs q-gutter-xs">
|
||||||
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
||||||
<code>--nosalt</code>
|
<code>-silent</code>
|
||||||
|
</q-badge>
|
||||||
|
<span>Do not popup any message boxes during install</span>
|
||||||
|
</div>
|
||||||
|
<div class="q-pa-xs q-gutter-xs">
|
||||||
|
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
||||||
|
<code>-nosalt</code>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
<span> Do not install salt during agent install. </span>
|
<span> Do not install salt during agent install. </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="q-pa-xs q-gutter-xs">
|
<div class="q-pa-xs q-gutter-xs">
|
||||||
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
||||||
<code>--local-mesh "C:\\<some folder or path>\\meshagent.exe"</code>
|
<code>-local-mesh "C:\\<some folder or path>\\meshagent.exe"</code>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
<span>
|
<span>
|
||||||
To skip downloading the Mesh Agent during the install. Download it
|
To skip downloading the Mesh Agent during the install. Download it
|
||||||
@@ -49,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="q-pa-xs q-gutter-xs">
|
<div class="q-pa-xs q-gutter-xs">
|
||||||
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
|
||||||
<code>--cert "C:\\<some folder or path>\\ca.pem"</code>
|
<code>-cert "C:\\<some folder or path>\\ca.pem"</code>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
<span> To use a domain CA </span>
|
<span> To use a domain CA </span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,10 +27,13 @@
|
|||||||
<p>Fix issues with the Tactical Checkrunner windows service which handles running all checks.</p>
|
<p>Fix issues with the Tactical Checkrunner windows service which handles running all checks.</p>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section v-show="mode === 'salt'">
|
<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>
|
||||||
<q-card-section v-show="mode === 'rpc'">
|
<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>
|
||||||
<q-card-section v-show="mode === 'command'">
|
<q-card-section v-show="mode === 'command'">
|
||||||
<p>Run a shell command on the agent.</p>
|
<p>Run a shell command on the agent.</p>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<q-btn
|
<q-btn
|
||||||
label="Cancel Action"
|
label="Cancel Action"
|
||||||
:disable="selectedRow === null || selectedStatus === 'completed' || actionType === 'taskaction'"
|
:disable="selectedRow === null || selectedStatus === 'completed' || actionType !== 'schedreboot'"
|
||||||
color="red"
|
color="red"
|
||||||
icon="cancel"
|
icon="cancel"
|
||||||
dense
|
dense
|
||||||
@@ -98,26 +98,26 @@ export default {
|
|||||||
hostname: "",
|
hostname: "",
|
||||||
pagination: {
|
pagination: {
|
||||||
rowsPerPage: 0,
|
rowsPerPage: 0,
|
||||||
sortBy: "due",
|
sortBy: "status",
|
||||||
descending: true,
|
descending: false,
|
||||||
},
|
},
|
||||||
all_columns: [
|
all_columns: [
|
||||||
{ name: "id", field: "id" },
|
{ name: "id", field: "id" },
|
||||||
{ name: "status", field: "status" },
|
{ name: "status", field: "status" },
|
||||||
{ name: "type", label: "Type", align: "left", sortable: true },
|
{ name: "type", label: "Type", field: "action_type", align: "left", sortable: true },
|
||||||
{ name: "due", label: "Due", field: "due", align: "left", sortable: true },
|
{ name: "due", label: "Due", field: "due", align: "left", sortable: true },
|
||||||
{ name: "desc", label: "Description", align: "left", sortable: true },
|
{ name: "desc", label: "Description", field: "description", align: "left", sortable: true },
|
||||||
{ name: "agent", label: "Agent", align: "left", sortable: true },
|
{ name: "agent", label: "Agent", field: "hostname", align: "left", sortable: true },
|
||||||
{ name: "client", label: "Client", align: "left", sortable: true },
|
{ name: "client", label: "Client", field: "client", align: "left", sortable: true },
|
||||||
{ name: "site", label: "Site", align: "left", sortable: true },
|
{ name: "site", label: "Site", field: "site", align: "left", sortable: true },
|
||||||
],
|
],
|
||||||
all_visibleColumns: ["type", "due", "desc", "agent", "client", "site"],
|
all_visibleColumns: ["type", "due", "desc", "agent", "client", "site"],
|
||||||
agent_columns: [
|
agent_columns: [
|
||||||
{ name: "id", field: "id" },
|
{ name: "id", field: "id" },
|
||||||
{ name: "status", field: "status" },
|
{ name: "status", field: "status" },
|
||||||
{ name: "type", label: "Type", align: "left", sortable: true },
|
{ name: "type", label: "Type", field: "action_type", align: "left", sortable: true },
|
||||||
{ name: "due", label: "Due", field: "due", align: "left", sortable: true },
|
{ name: "due", label: "Due", field: "due", align: "left", sortable: true },
|
||||||
{ name: "desc", label: "Description", align: "left", sortable: true },
|
{ name: "desc", label: "Description", field: "description", align: "left", sortable: true },
|
||||||
],
|
],
|
||||||
agent_visibleColumns: ["type", "due", "desc"],
|
agent_visibleColumns: ["type", "due", "desc"],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user