optimize the cache_db_values task

This commit is contained in:
sadnub 2022-04-16 14:36:31 -04:00
parent e09c307d58
commit e9d71f169c
6 changed files with 79 additions and 37 deletions

View File

@ -0,0 +1,21 @@
# Generated by Django 4.0.3 on 2022-04-16 17:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('agents', '0047_alter_agent_plat_alter_agent_site'),
]
operations = [
migrations.RemoveField(
model_name='agent',
name='has_patches_pending',
),
migrations.RemoveField(
model_name='agent',
name='pending_actions_count',
),
]

View File

@ -68,8 +68,6 @@ class Agent(BaseAuditModel):
)
maintenance_mode = models.BooleanField(default=False)
block_policy_inheritance = models.BooleanField(default=False)
pending_actions_count = models.PositiveIntegerField(default=0)
has_patches_pending = models.BooleanField(default=False)
alert_template = models.ForeignKey(
"alerts.AlertTemplate",
related_name="agents",
@ -351,23 +349,10 @@ class Agent(BaseAuditModel):
checks = list(self.agentchecks.all()) + self.get_checks_from_policies()
return self.add_check_results(checks)
def get_tasks_with_policies(
self, exclude_synced: bool = False
) -> "List[AutomatedTask]":
from autotasks.models import TaskResult
def get_tasks_with_policies(self) -> "List[AutomatedTask]":
tasks = list(self.autotasks.all()) + self.get_tasks_from_policies()
if exclude_synced:
return [
task
for task in tasks
if not task.task_result
or isinstance(task.task_result, TaskResult)
and task.task_result.sync_status != "synced"
]
else:
return self.add_task_results(tasks)
return self.add_task_results(tasks)
def add_task_results(self, tasks: "List[AutomatedTask]") -> "List[AutomatedTask]":

View File

@ -86,6 +86,8 @@ class AgentTableSerializer(serializers.ModelSerializer):
policy = serializers.ReadOnlyField(source="policy.id")
alert_template = serializers.SerializerMethodField()
last_seen = serializers.ReadOnlyField()
pending_actions_count = serializers.ReadOnlyField()
has_pending_patches = serializers.ReadOnlyField()
def get_alert_template(self, obj):
@ -121,7 +123,6 @@ class AgentTableSerializer(serializers.ModelSerializer):
"monitoring_type",
"description",
"needs_reboot",
"has_patches_pending",
"pending_actions_count",
"status",
"overdue_text_alert",
@ -137,6 +138,7 @@ class AgentTableSerializer(serializers.ModelSerializer):
"block_policy_inheritance",
"plat",
"goarch",
"has_pending_patches",
]
depth = 2

View File

@ -14,7 +14,7 @@ from core.utils import (
get_core_settings,
)
from django.conf import settings
from django.db.models import Q, Prefetch, F
from django.db.models import Q, Prefetch, Exists, Count
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone as djangotime
@ -29,6 +29,7 @@ from scripts.models import Script
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
from winupdate.serializers import WinUpdatePolicySerializer
from winupdate.tasks import bulk_check_for_updates_task, bulk_install_updates_task
from winupdate.models import WinUpdate
from tacticalrmm.constants import AGENT_DEFER
from tacticalrmm.permissions import (
@ -107,6 +108,12 @@ class GetAgents(APIView):
queryset=CheckResult.objects.select_related("assigned_check"),
),
)
.annotate(pending_actions_count=Count("pendingactions"))
.annotate(
has_pending_patches=Exists(
WinUpdate.objects.filter(action="approve", installed=False)
)
)
)
ctx = {"default_tz": get_default_timezone()}
serializer = AgentTableSerializer(agents, many=True, context=ctx)

View File

@ -7,8 +7,11 @@ from alerts.tasks import prune_resolved_alerts
from autotasks.models import TaskResult
from checks.tasks import prune_check_history
from clients.models import Client, Site
from checks.models import Check, CheckResult
from core.utils import get_core_settings
from django.conf import settings
from django.db.models import Prefetch, Exists, OuterRef
from logs.models import PendingAction
from logs.tasks import prune_audit_log, prune_debug_log
from packaging import version as pyver
@ -96,18 +99,51 @@ def _get_failing_data(agents: "QuerySet[Any]") -> Dict[str, bool]:
@app.task
def cache_db_fields_task() -> None:
agent_queryset = (
Agent.objects.defer(*AGENT_DEFER)
.select_related(
"site__server_policy",
"site__workstation_policy",
"site__client__server_policy",
"site__client__workstation_policy",
"policy",
"alert_template",
)
.prefetch_related(
Prefetch(
"agentchecks",
queryset=Check.objects.select_related("script"),
),
Prefetch(
"checkresults",
queryset=CheckResult.objects.select_related("assigned_check"),
),
"autotasks",
Prefetch(
"taskresults",
queryset=TaskResult.objects.select_related("task"),
),
)
)
# update client/site failing check fields and agent counts
for site in Site.objects.all():
agents = site.agents.defer(*AGENT_DEFER)
agents = agent_queryset.filter(site=site)
site.failing_checks = _get_failing_data(agents)
site.save(update_fields=["failing_checks"])
for client in Client.objects.all():
agents = Agent.objects.defer(*AGENT_DEFER).filter(site__client=client)
agents = agent_queryset.filter(site__client=client)
client.failing_checks = _get_failing_data(agents)
client.save(update_fields=["failing_checks"])
for agent in Agent.objects.defer(*AGENT_DEFER):
for agent in agent_queryset.annotate(
has_pending_actions=Exists(
PendingAction.objects.filter(
pk=OuterRef("pk"), action_type="agent_update", status="pending"
)
)
):
if (
pyver.parse(agent.version) >= pyver.parse("1.6.0")
and agent.status == "online"
@ -115,34 +151,25 @@ def cache_db_fields_task() -> None:
# change agent update pending status to completed if agent has just updated
if (
pyver.parse(agent.version) == pyver.parse(settings.LATEST_AGENT_VER)
and agent.pendingactions.filter(
action_type="agentupdate", status="pending"
).exists()
and agent.has_pending_actions
):
agent.pendingactions.filter(
action_type="agentupdate", status="pending"
action_type="agent_update", status="pending"
).update(status="completed")
# sync scheduled tasks
for task in agent.get_tasks_with_policies(exclude_synced=True):
for task in agent.get_tasks_with_policies():
if not task.task_result or task.task_result.sync_status == "initial":
task.create_task_on_agent(agent=agent if task.policy else None)
elif task.task_result.sync_status == "pendingdeletion":
task.delete_task_on_agent(agent=agent if task.policy else None)
elif task.task_result.sync_status == "notsynced":
task.modify_task_on_agent(agent=agent if task.policy else None)
elif task.task_result.sync_status == "synced":
continue
# handles any alerting actions
if Alert.objects.filter(
alert_type="availability", agent=agent, resolved=False
).exists():
Alert.handle_alert_resolve(agent)
# update pending patches and pending action counts
agent.pending_actions_count = agent.pendingactions.filter(
status="pending"
).count()
agent.has_patches_pending = (
agent.winupdates.filter(action="approve").filter(installed=False).exists()
)
agent.save(update_fields=["pending_actions_count", "has_patches_pending"])

View File

@ -184,7 +184,7 @@
</q-td>
<q-td :props="props" key="pendingactions">
<q-icon
v-if="props.row.pending_actions_count !== 0"
v-if="props.row.pending_actions_count > 0"
@click="showPendingActionsModal(props.row)"
name="far fa-clock"
size="1.4em"