Skip to content

Commit

Permalink
Release 0.10.3
Browse files Browse the repository at this point in the history
  • Loading branch information
wh1te909 committed Dec 2, 2021
2 parents 2eefeda + 9c0993d commit da54e97
Show file tree
Hide file tree
Showing 58 changed files with 1,711 additions and 692 deletions.
1 change: 1 addition & 0 deletions .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ Pygments
mypy
pysnooper
isort
drf_spectacular
81 changes: 81 additions & 0 deletions api/tacticalrmm/agents/management/commands/bulk_delete_agents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import asyncio

from django.core.management.base import BaseCommand
from django.utils import timezone as djangotime
from packaging import version as pyver

from agents.models import Agent
from tacticalrmm.utils import AGENT_DEFER, reload_nats


class Command(BaseCommand):
help = "Delete old agents"

def add_arguments(self, parser):
parser.add_argument(
"--days",
type=int,
help="Delete agents that have not checked in for this many days",
)
parser.add_argument(
"--agentver",
type=str,
help="Delete agents that equal to or less than this version",
)
parser.add_argument(
"--delete",
action="store_true",
help="This will delete agents",
)

def handle(self, *args, **kwargs):
days = kwargs["days"]
agentver = kwargs["agentver"]
delete = kwargs["delete"]

if not days and not agentver:
self.stdout.write(
self.style.ERROR("Must have at least one parameter: days or agentver")
)
return

q = Agent.objects.defer(*AGENT_DEFER)

agents = []
if days:
overdue = djangotime.now() - djangotime.timedelta(days=days)
agents = [i for i in q if i.last_seen < overdue]

if agentver:
agents = [i for i in q if pyver.parse(i.version) <= pyver.parse(agentver)]

if not agents:
self.stdout.write(self.style.ERROR("No agents matched"))
return

deleted_count = 0
for agent in agents:
s = f"{agent.hostname} | Version {agent.version} | Last Seen {agent.last_seen} | {agent.client} > {agent.site}"
if delete:
s = "Deleting " + s
self.stdout.write(self.style.SUCCESS(s))
asyncio.run(agent.nats_cmd({"func": "uninstall"}, wait=False))
try:
agent.delete()
except Exception as e:
err = f"Failed to delete agent {agent.hostname}: {str(e)}"
self.stdout.write(self.style.ERROR(err))
else:
deleted_count += 1
else:
self.stdout.write(self.style.WARNING(s))

if delete:
reload_nats()
self.stdout.write(self.style.SUCCESS(f"Deleted {deleted_count} agents"))
else:
self.stdout.write(
self.style.SUCCESS(
"The above agents would be deleted. Run again with --delete to actually delete them."
)
)
5 changes: 5 additions & 0 deletions api/tacticalrmm/agents/management/commands/update_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from packaging import version as pyver

from agents.models import Agent
from core.models import CoreSettings
from agents.tasks import send_agent_update_task
from tacticalrmm.utils import AGENT_DEFER

Expand All @@ -11,6 +12,10 @@ class Command(BaseCommand):
help = "Triggers an agent update task to run"

def handle(self, *args, **kwargs):
core = CoreSettings.objects.first()
if not core.agent_auto_update: # type: ignore
return

q = Agent.objects.defer(*AGENT_DEFER).exclude(version=settings.LATEST_AGENT_VER)
agent_ids: list[str] = [
i.agent_id
Expand Down
6 changes: 1 addition & 5 deletions api/tacticalrmm/agents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def save(self, *args, **kwargs):

# check if new agent has been created
# or check if policy have changed on agent
# or if site has changed on agent and if so generate-policies
# or if site has changed on agent and if so generate policies
# or if agent was changed from server or workstation
if (
not old_agent
Expand All @@ -109,10 +109,6 @@ def save(self, *args, **kwargs):
):
generate_agent_checks_task.delay(agents=[self.pk], create_tasks=True)

# calculate alert template for new agents
if not old_agent:
self.set_alert_template()

def __str__(self):
return self.hostname

Expand Down
5 changes: 2 additions & 3 deletions api/tacticalrmm/agents/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from agents.models import Agent
from agents.utils import get_winagent_url
from tacticalrmm.utils import AGENT_DEFER


def agent_update(agent_id: str, force: bool = False) -> str:
Expand Down Expand Up @@ -311,9 +312,7 @@ def prune_agent_history(older_than_days: int) -> str:

@app.task
def handle_agents_task() -> None:
q = Agent.objects.prefetch_related("pendingactions", "autotasks").only(
"pk", "agent_id", "version", "last_seen", "overdue_time", "offline_time"
)
q = Agent.objects.defer(*AGENT_DEFER)
agents = [
i
for i in q
Expand Down
3 changes: 2 additions & 1 deletion api/tacticalrmm/alerts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ def parse_script_args(self, args: list[str]):
if match:
name = match.group(1)

if hasattr(self, name):
# check if attr exists and isn't a function
if hasattr(self, name) and not callable(getattr(self, name)):
value = f"'{getattr(self, name)}'"
else:
continue
Expand Down
14 changes: 7 additions & 7 deletions api/tacticalrmm/apiv3/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,18 @@ class WinUpdates(APIView):

def put(self, request):
agent = get_object_or_404(Agent, agent_id=request.data["agent_id"])

needs_reboot: bool = request.data["needs_reboot"]
agent.needs_reboot = needs_reboot
agent.save(update_fields=["needs_reboot"])

reboot_policy: str = agent.get_patch_policy().reboot_after_install
reboot = False

if reboot_policy == "always":
reboot = True

if request.data["needs_reboot"]:
if reboot_policy == "required":
reboot = True
elif reboot_policy == "never":
agent.needs_reboot = True
agent.save(update_fields=["needs_reboot"])
elif needs_reboot and reboot_policy == "required":
reboot = True

if reboot:
asyncio.run(agent.nats_cmd({"func": "rebootnow"}, wait=False))
Expand Down
2 changes: 2 additions & 0 deletions api/tacticalrmm/automation/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def generate_agent_checks_task(
if create_tasks:
agent.generate_tasks_from_policies()

agent.set_alert_template()

return "ok"


Expand Down
12 changes: 12 additions & 0 deletions api/tacticalrmm/core/management/commands/post_update_tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
from django.core.management.base import BaseCommand

from logs.models import PendingAction
Expand All @@ -20,3 +21,14 @@ def handle(self, *args, **kwargs):
for user in User.objects.filter(is_installer_user=True):
user.block_dashboard_login = True
user.save()

# convert script base64 field to text field
user_scripts = Script.objects.exclude(script_type="builtin").filter(
script_body=""
)
for script in user_scripts:
# decode base64 string
script.script_body = base64.b64decode(
script.code_base64.encode("ascii", "ignore")
).decode("ascii", "ignore")
script.hash_script_body() # also saves script
5 changes: 2 additions & 3 deletions api/tacticalrmm/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from core.models import CoreSettings
from logs.tasks import prune_debug_log, prune_audit_log
from tacticalrmm.celery import app
from tacticalrmm.utils import AGENT_DEFER


@app.task
Expand Down Expand Up @@ -58,9 +59,7 @@ def core_maintenance_tasks():
def cache_db_fields_task():
from agents.models import Agent

for agent in Agent.objects.prefetch_related("winupdates", "pendingactions").only(
"pending_actions_count", "has_patches_pending", "pk"
):
for agent in Agent.objects.defer(*AGENT_DEFER):
agent.pending_actions_count = agent.pendingactions.filter(
status="pending"
).count()
Expand Down
12 changes: 9 additions & 3 deletions api/tacticalrmm/logs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.exceptions import PermissionDenied
from tacticalrmm.utils import notify_error, get_default_timezone
from tacticalrmm.utils import notify_error, get_default_timezone, AGENT_DEFER
from tacticalrmm.permissions import _audit_log_filter, _has_perm_on_agent

from .models import AuditLog, PendingAction, DebugLog
Expand Down Expand Up @@ -93,10 +93,16 @@ class PendingActions(APIView):

def get(self, request, agent_id=None):
if agent_id:
agent = get_object_or_404(Agent, agent_id=agent_id)
agent = get_object_or_404(
Agent.objects.defer(*AGENT_DEFER), agent_id=agent_id
)
actions = PendingAction.objects.filter(agent=agent)
else:
actions = PendingAction.objects.filter_by_role(request.user)
actions = (
PendingAction.objects.select_related("agent")
.defer("agent__services", "agent__wmi_detail")
.filter_by_role(request.user) # type: ignore
)

return Response(PendingActionSerializer(actions, many=True).data)

Expand Down
1 change: 0 additions & 1 deletion api/tacticalrmm/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ Pygments
isort
mypy
types-pytz
types-pytz
4 changes: 2 additions & 2 deletions api/tacticalrmm/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
asgiref==3.4.1
asyncio-nats-client==0.11.4
asyncio-nats-client==0.11.5
celery==5.2.1
certifi==2021.10.8
cffi==1.15.0
Expand All @@ -15,7 +15,7 @@ django-rest-knox==4.1.0
djangorestframework==3.12.4
future==0.18.2
loguru==0.5.3
msgpack==1.0.2
msgpack==1.0.3
packaging==21.3
psycopg2-binary==2.9.2
pycparser==2.21
Expand Down

0 comments on commit da54e97

Please sign in to comment.