add handle alerts functions to agents, checks, and tasks. Minor fixes

This commit is contained in:
sadnub
2021-02-05 17:33:40 -05:00
parent 5e7ebf5e69
commit 5dbfb64822
25 changed files with 789 additions and 428 deletions

View File

@@ -409,65 +409,88 @@ class Agent(BaseAuditModel):
site = self.site
client = self.client
core = CoreSettings.objects.first()
# check if alert template is on a policy assigned to agent and return
if self.policy and self.policy.alert_template:
return self.policy.alert_template
# check if policy with alert template is assigned to the site and return
templates = list()
# check if alert template is on a policy assigned to agent
if (
self.policy
and self.policy.alert_template
and self.policy.alert_template.is_active
):
templates.append(self.policy.alert_template)
# check if policy with alert template is assigned to the site
elif (
self.monitoring_type == "server"
and site.server_policy
and site.server_policy.alert_template
and site.server_policy.alert_template.is_active
):
return site.server_policy.alert_template
templates.append(site.server_policy.alert_template)
elif (
self.monitoring_type == "workstation"
and site.workstation_policy
and site.workstation_policy.alert_template
and site.workstation_policy.alert_template.is_active
):
return site.workstation_policy.alert_template
templates.append(site.workstation_policy.alert_template)
# check if alert template is assigned to site and return
elif site.alert_template:
return site.alert_template
# check if alert template is assigned to site
elif site.alert_template and site.alert_template.is_active:
templates.append(site.alert_template)
# check if policy with alert template is assigned to the client and return
# check if policy with alert template is assigned to the client
elif (
self.monitoring_type == "server"
and client.server_policy
and client.server_policy.alert_template
and client.server_policy.alert_template.is_active
):
return client.server_policy.alert_template
templates.append(client.server_policy.alert_template)
elif (
self.monitoring_type == "workstation"
and client.workstation_policy
and client.workstation_policy.alert_template
and client.workstation_policy.alert_template.is_active
):
return client.workstation_policy.alert_template
templates.append(client.workstation_policy.alert_template)
# check if alert template is on client and return
elif client.alert_template:
return client.alert_template
elif client.alert_template and client.alert_template.is_active:
templates.append(client.alert_template)
# check if alert template is applied globally and return
elif core.alert_template:
return core.alert_template
elif core.alert_template and core.alert_template.is_active:
templates.append(core.alert_template)
# if agent is a workstation, check if policy with alert template is assigned to the site, client, or core
elif (
self.monitoring_type == "server"
and core.server_policy
and core.server_policy.alert_template
and core.server_policy.alert_template.is_active
):
return core.server_policy.alert_template
templates.append(core.server_policy.alert_template)
elif (
self.monitoring_type == "workstation"
and core.workstation_policy
and core.workstation_policy.alert_template
and core.workstation_policy.alert_template.is_active
):
return core.workstation_policy.alert_template
templates.append(core.workstation_policy.alert_template)
# nothing found returning None
# check if client, site, or agent has been excluded from templates in order and return if not
for template in templates:
if (
client.pk in template.excluded_clients.all()
or site.pk in template.excluded_sites.all()
or self.pk in template.excluded_agents.all()
):
continue
else:
return template
# no alert templates found or agent has been excluded
return None
def generate_checks_from_policies(self):
@@ -609,6 +632,88 @@ class Agent(BaseAuditModel):
if action.details["task_id"] == task_id:
action.delete()
def handle_alert(self, checkin: bool = False) -> None:
from alerts.models import Alert
from agents.models import AgentOutage
from agents.tasks import (
agent_recovery_email_task,
agent_recovery_sms_task,
agent_outage_email_task,
agent_outage_sms_task,
)
# return if agent is in maintenace mode
if self.maintenance_mode:
return
alert_template = self.get_alert_template()
# called when agent is back online
if checkin:
if self.agentoutages.exists() and self.agentoutages.last().is_active:
# resolve any open outages
self.agentoutages.filter(recovery_time=None).update(
recovery_time=djangotime.now()
)
last_outage = agent.agentoutages.last()
# resolve alert if exists
if Alert.objects.filter(agent=self, resolved=False).exists():
alert = Alert.objects.get(agent=self, resolved=False)
alert.resolve()
# check if a resolved notification should be emailed
if (
alert_template
and alert_template.agent_email_on_resolved
or self.overdue_email_alert
):
agent_recovery_email_task.delay(pk=last_outage.pk)
# check if a resolved notification should be texted
if (
alert_template
and alert_template.agent_text_on_resolved
or self.overdue_text_alert
):
agent_recovery_sms_task.delay(pk=last_outage.pk)
# called when agent is offline
else:
# return if outage has already been created
if self.agentoutages.exists() and self.agentoutages.last().is_active:
return
outage = AgentOutage(agent=self)
outage.save()
# add a null check history to allow gaps in graph
for check in self.agentchecks.all():
check.add_check_history(None)
# create dashboard alert if enabled
if (
alert_template
and alert_template.agent_always_alert
or self.overdue_dashboard_alert
):
Alert.create_availability_alert(self)
# send email alert if enabled
if (
alert_template
and alert_template.agent_always_email
or self.overdue_email_alert
):
agent_outage_email_task.delay(pk=outage.pk)
if (
alert_template
and alert_template.agent_always_text
or self.overdue_text_alert
):
agent_outage_sms_task.delay(pk=outage.pk)
class AgentOutage(models.Model):
agent = models.ForeignKey(

View File

@@ -106,7 +106,7 @@ def auto_self_agent_update_task() -> None:
@app.task
def agent_outage_email_task(pk):
def agent_outage_email_task(pk) -> None:
sleep(random.randint(1, 15))
outage = AgentOutage.objects.get(pk=pk)
outage.send_outage_email()
@@ -115,7 +115,7 @@ def agent_outage_email_task(pk):
@app.task
def agent_recovery_email_task(pk):
def agent_recovery_email_task(pk) -> None:
sleep(random.randint(1, 15))
outage = AgentOutage.objects.get(pk=pk)
outage.send_recovery_email()
@@ -124,7 +124,7 @@ def agent_recovery_email_task(pk):
@app.task
def agent_outage_sms_task(pk):
def agent_outage_sms_task(pk) -> None:
sleep(random.randint(1, 3))
outage = AgentOutage.objects.get(pk=pk)
outage.send_outage_sms()
@@ -133,7 +133,7 @@ def agent_outage_sms_task(pk):
@app.task
def agent_recovery_sms_task(pk):
def agent_recovery_sms_task(pk) -> None:
sleep(random.randint(1, 3))
outage = AgentOutage.objects.get(pk=pk)
outage.send_recovery_sms()
@@ -142,35 +142,19 @@ def agent_recovery_sms_task(pk):
@app.task
def agent_outages_task():
def agent_outages_task() -> None:
agents = Agent.objects.only(
"pk", "last_seen", "overdue_time", "overdue_email_alert", "overdue_text_alert"
"pk",
"last_seen",
"overdue_time",
"overdue_email_alert",
"overdue_text_alert",
"overdue_dashboard_alert",
)
for agent in agents:
if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue":
from alerts.models import Alert
outages = AgentOutage.objects.filter(agent=agent)
if outages and outages.last().is_active:
continue
outage = AgentOutage(agent=agent)
outage.save()
# create dashboard alert
Alert.create_availability_alert(agent)
# add a null check history to allow gaps in graph
for check in agent.agentchecks.all():
check.add_check_history(None)
if agent.overdue_email_alert and not agent.maintenance_mode:
agent_outage_email_task.delay(pk=outage.pk)
if agent.overdue_text_alert and not agent.maintenance_mode:
agent_outage_sms_task.delay(pk=outage.pk)
if agent.status == "overdue":
agent.handle_alert()
@app.task

View File

@@ -0,0 +1,25 @@
# Generated by Django 3.1.4 on 2021-02-05 16:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('alerts', '0014_auto_20210202_0312'),
]
operations = [
migrations.RemoveField(
model_name='alerttemplate',
name='agent_alert_on_resolved',
),
migrations.RemoveField(
model_name='alerttemplate',
name='check_alert_on_resolved',
),
migrations.RemoveField(
model_name='alerttemplate',
name='task_alert_on_resolved',
),
]

View File

@@ -61,37 +61,37 @@ class Alert(models.Model):
self.save()
@classmethod
def create_availability_alert(cls, agent, severity="error") -> None:
def create_availability_alert(cls, agent) -> None:
if not cls.objects.filter(agent=agent, resolved=False).exists():
cls.objects.create(
agent=agent,
alert_type="availability",
severity=severity,
message=f"{agent.hostname} in {agent.client.name}\{agent.site.name} is Offline.",
severity="error",
message=f"{agent.hostname} in {agent.client.name}\\{agent.site.name} is Offline.",
)
@classmethod
def create_check_alert(cls, check, severity="error") -> None:
def create_check_alert(cls, check) -> None:
if not cls.objects.filter(assigned_check=check, resolved=False).exists():
cls.objects.create(
assigned_check=check,
agent=check.agent,
alert_type="check",
severity=severity,
message=f"{check.agent.hostname} has {check.check_type} check that failed.",
severity=check.alert_severity,
message=f"{check.agent.hostname} has a {check.check_type} check: {check.readable_desc} that failed.",
)
@classmethod
def create_task_alert(cls, task, severity="error") -> None:
def create_task_alert(cls, task) -> None:
if not cls.objects.filter(assigned_task=task, resolved=False).exists():
cls.objects.create(
assigned_task=task,
agent=task.agent,
alert_type="task",
severity=severity,
message=f"{task.agent.hostname} has task that failed.",
severity=task.alert_severity,
message=f"{task.agent.hostname} has task: {task.name} that failed.",
)
@classmethod
@@ -126,7 +126,6 @@ class AlertTemplate(models.Model):
# agent alert settings
agent_email_on_resolved = BooleanField(null=True, blank=True, default=False)
agent_text_on_resolved = BooleanField(null=True, blank=True, default=False)
agent_alert_on_resolved = BooleanField(null=True, blank=True, default=False)
agent_include_desktops = BooleanField(null=True, blank=True, default=False)
agent_always_email = BooleanField(null=True, blank=True, default=False)
agent_always_text = BooleanField(null=True, blank=True, default=False)
@@ -151,7 +150,6 @@ class AlertTemplate(models.Model):
)
check_email_on_resolved = BooleanField(null=True, blank=True, default=False)
check_text_on_resolved = BooleanField(null=True, blank=True, default=False)
check_alert_on_resolved = BooleanField(null=True, blank=True, default=False)
check_always_email = BooleanField(null=True, blank=True, default=False)
check_always_text = BooleanField(null=True, blank=True, default=False)
check_always_alert = BooleanField(null=True, blank=True, default=False)
@@ -175,7 +173,6 @@ class AlertTemplate(models.Model):
)
task_email_on_resolved = BooleanField(null=True, blank=True, default=False)
task_text_on_resolved = BooleanField(null=True, blank=True, default=False)
task_alert_on_resolved = BooleanField(null=True, blank=True, default=False)
task_always_email = BooleanField(null=True, blank=True, default=False)
task_always_text = BooleanField(null=True, blank=True, default=False)
task_always_alert = BooleanField(null=True, blank=True, default=False)
@@ -199,7 +196,6 @@ class AlertTemplate(models.Model):
return (
self.agent_email_on_resolved
or self.agent_text_on_resolved
or self.agent_alert_on_resolved
or self.agent_include_desktops
or self.agent_always_email
or self.agent_always_text
@@ -215,7 +211,6 @@ class AlertTemplate(models.Model):
or bool(self.check_dashboard_alert_severity)
or self.check_email_on_resolved
or self.check_text_on_resolved
or self.check_alert_on_resolved
or self.check_always_email
or self.check_always_text
or self.check_always_alert
@@ -230,13 +225,16 @@ class AlertTemplate(models.Model):
or bool(self.task_dashboard_alert_severity)
or self.task_email_on_resolved
or self.task_text_on_resolved
or self.task_alert_on_resolved
or self.task_always_email
or self.task_always_text
or self.task_always_alert
or bool(self.task_periodic_alert_days)
)
@property
def has_core_settings(self) -> bool:
return bool(self.email_from) or self.email_recipients or self.text_recipients
@property
def is_default_template(self) -> bool:
return self.default_alert_template.exists()

View File

@@ -36,6 +36,7 @@ class AlertTemplateSerializer(ModelSerializer):
agent_settings = ReadOnlyField(source="has_agent_settings")
check_settings = ReadOnlyField(source="has_check_settings")
task_settings = ReadOnlyField(source="has_task_settings")
core_settings = ReadOnlyField(source="has_core_settings")
default_template = ReadOnlyField(source="is_default_template")
applied_count = SerializerMethodField()

View File

@@ -1,6 +1,4 @@
import asyncio
import os
import requests
from loguru import logger
from packaging import version as pyver
@@ -22,15 +20,8 @@ from accounts.models import User
from winupdate.models import WinUpdatePolicy
from software.models import InstalledSoftware
from checks.serializers import CheckRunnerGetSerializer
from agents.serializers import WinAgentSerializer
from autotasks.serializers import TaskGOGetSerializer, TaskRunnerPatchSerializer
from winupdate.serializers import ApprovedUpdateSerializer
from agents.tasks import (
agent_recovery_email_task,
agent_recovery_sms_task,
)
from checks.utils import bytes2human
from tacticalrmm.utils import notify_error, reload_nats, filter_software, SoftwareList
logger.configure(**settings.LOG_CONFIG)
@@ -98,25 +89,9 @@ class TaskRunner(APIView):
serializer.is_valid(raise_exception=True)
serializer.save(last_run=djangotime.now())
# create alert in dashboard if retcode is not 0
if task.alert_severity and request.data["retcode"] > 0:
from alerts.models import Alert
Alert.create_task_alert(task, task.alert_severity)
# TODO: send email/text alert if configured
# resolve alert if passing
elif task.alert_severity and request.data["retcode"] == 0:
from alerts.models import Alert
if Alert.objects.filter(assigned_task=task, resolved=False).exists():
alert = Alert.objects.get(assigned_task=task, resolved=False)
alert.resolve()
# TODO: send resolved email/text alert if configured
new_task = AutomatedTask.objects.get(pk=task.pk)
new_task.handle_alert()
AuditLog.objects.create(
username=agent.hostname,
agent=agent.hostname,

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.1.4 on 2021-02-05 17:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('autotasks', '0014_automatedtask_dashboard_alert'),
]
operations = [
migrations.AddField(
model_name='automatedtask',
name='resolved_email_sent',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='automatedtask',
name='resolved_text_sent',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2021-02-05 21:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('autotasks', '0015_auto_20210205_1728'),
]
operations = [
migrations.AddField(
model_name='automatedtask',
name='status',
field=models.CharField(choices=[('passing', 'Passing'), ('failing', 'Failing'), ('pending', 'Pending')], default='pending', max_length=30),
),
]

View File

@@ -8,6 +8,7 @@ from django.contrib.postgres.fields import ArrayField
from django.db.models.fields import DateTimeField
from logs.models import BaseAuditModel
from tacticalrmm.utils import bitdays_to_string
from typing import Union
from alerts.models import SEVERITY_CHOICES
@@ -34,6 +35,12 @@ SYNC_STATUS_CHOICES = [
("pendingdeletion", "Pending Deletion on Agent"),
]
TASK_STATUS_CHOICES = [
("passing", "Passing"),
("failing", "Failing"),
("pending", "Pending"),
]
class AutomatedTask(BaseAuditModel):
agent = models.ForeignKey(
@@ -95,6 +102,9 @@ class AutomatedTask(BaseAuditModel):
execution_time = models.CharField(max_length=100, default="0.0000")
last_run = models.DateTimeField(null=True, blank=True)
enabled = models.BooleanField(default=True)
status = models.CharField(
max_length=30, choices=TASK_STATUS_CHOICES, default="pending"
)
sync_status = models.CharField(
max_length=100, choices=SYNC_STATUS_CHOICES, default="notsynced"
)
@@ -106,6 +116,8 @@ class AutomatedTask(BaseAuditModel):
dashboard_alert = models.BooleanField(default=False)
email_sent = models.DateTimeField(null=True, blank=True)
text_sent = models.DateTimeField(null=True, blank=True)
resolved_email_sent = models.DateTimeField(null=True, blank=True)
resolved_text_sent = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.name
@@ -150,16 +162,40 @@ class AutomatedTask(BaseAuditModel):
def create_policy_task(self, agent=None, policy=None):
from .tasks import create_win_task_schedule
# if policy is present, then this task is being copied to another policy
# if agent is present, then this task is being created on an agent from a policy
# exit if neither are set or if both are set
if not agent and not policy or agent and policy:
return
assigned_check = None
# get correct assigned check to task if set
if agent and self.assigned_check:
assigned_check = agent.agentchecks.get(parent_check=self.assigned_check.pk)
# check if there is a matching check on the agent
if agent.agentchecks.filter(parent_check=self.assigned_check.pk).exists():
assigned_check = agent.agentchecks.filter(
parent_check=self.assigned_check.pk
)
# check was overriden by agent and we need to use that agents check
else:
if agent.agentchecks.filter(
check_type=self.assigned_check.check_type, overriden_by_policy=True
).exists():
assigned_check = agent.agentchecks.filter(
check_type=self.assigned_check.check_type,
overriden_by_policy=True,
).first()
elif policy and self.assigned_check:
assigned_check = policy.policychecks.get(name=self.assigned_check.name)
if policy.policychecks.filter(name=self.assigned_check.name).exists():
assigned_check = policy.policychecks.filter(
name=self.assigned_check.name
)
else:
assigned_check = policy.policychecks.filter(
check_type=self.assigned_check.check_type
)
task = AutomatedTask.objects.create(
agent=agent,
@@ -186,3 +222,51 @@ class AutomatedTask(BaseAuditModel):
)
create_win_task_schedule.delay(task.pk)
def handle_alert(self) -> None:
from alerts.models import Alert, AlertTemplate
self.status = "failing" if self.retcode != 0 else "passing"
# return if agent is in maintenance mode
if self.agent.maintenance_mode:
return
# see if agent has an alert template and use that
alert_template: Union[AlertTemplate, None] = self.agent.get_alert_template()
# resolve alert if it exists
if self.status == "passing":
if Alert.objects.filter(assigned_task=self, resolved=False).exists():
alert = Alert.objects.get(assigned_task=self, resolved=False)
alert.resolve()
# check if a resolved notification should be send
if alert_template:
if (
not self.resolved_email_sent
and alert_template.task_email_on_resolved
):
# TODO: send email on resolved
pass
if not self.resolved_text_sent and alert_template.task_text_on_resolved:
# TODO: send text on resolved
pass
else:
# create alert in dashboard if enabled
if (
self.dashboard_alert
or alert_template
and alert_template.task_always_alert
):
Alert.create_task_alert(self)
# send email if enabled
if self.email_alert or alert_template and alert_template.check_always_email:
handle_task_email_alert_task.delay(self.pk)
# send text if enabled
if self.text_alert or alert_template and alert_template.check_always_text:
handle_task_sms_alert_task.delay(self.pk)
self.save()

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2021-02-05 16:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('checks', '0017_check_dashboard_alert'),
]
operations = [
migrations.AlterField(
model_name='check',
name='alert_severity',
field=models.CharField(blank=True, choices=[('info', 'Informational'), ('warning', 'Warning'), ('error', 'Error')], default='warning', max_length=15, null=True),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.1.4 on 2021-02-05 17:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('checks', '0018_auto_20210205_1647'),
]
operations = [
migrations.AddField(
model_name='check',
name='resolved_email_sent',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='check',
name='resolved_text_sent',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -10,10 +10,17 @@ from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.validators import MinValueValidator, MaxValueValidator
from rest_framework.fields import JSONField
from typing import List, Any
from typing import Union
from core.models import CoreSettings
from logs.models import BaseAuditModel
from .tasks import handle_check_email_alert_task, handle_check_sms_alert_task
from .tasks import (
handle_check_email_alert_task,
handle_check_sms_alert_task,
handle_resolved_check_email_alert_task,
handle_resolved_check_sms_alert_task,
)
from .utils import bytes2human
from alerts.models import SEVERITY_CHOICES
@@ -90,13 +97,19 @@ class Check(BaseAuditModel):
fail_count = models.PositiveIntegerField(default=0)
email_sent = models.DateTimeField(null=True, blank=True)
text_sent = models.DateTimeField(null=True, blank=True)
resolved_email_sent = models.DateTimeField(null=True, blank=True)
resolved_text_sent = models.DateTimeField(null=True, blank=True)
outage_history = models.JSONField(null=True, blank=True) # store
extra_details = models.JSONField(null=True, blank=True)
# check specific fields
# for eventlog, script, ip, and service alert severity
alert_severity = models.CharField(
max_length=15, choices=SEVERITY_CHOICES, default="warning"
max_length=15,
choices=SEVERITY_CHOICES,
default="warning",
null=True,
blank=True,
)
# threshold percent for diskspace, cpuload or memory check
@@ -222,7 +235,7 @@ class Check(BaseAuditModel):
return self.last_run
@property
def non_editable_fields(self):
def non_editable_fields(self) -> List[str]:
return [
"check_type",
"status",
@@ -249,14 +262,81 @@ class Check(BaseAuditModel):
"modified_time",
]
def add_check_history(self, value, more_info=None):
def handle_alert(self) -> None:
from alerts.models import Alert, AlertTemplate
# return if agent is in maintenance mode
if self.agent.maintenance_mode:
return
# see if agent has an alert template and use that
alert_template: Union[AlertTemplate, None] = self.agent.get_alert_template()
# resolve alert if it exists
if self.status == "passing":
if Alert.objects.filter(assigned_check=self, resolved=False).exists():
alert = Alert.objects.get(assigned_check=self, resolved=False)
alert.resolve()
# check if a resolved notification should be send
if alert_template:
if (
not self.resolved_email_sent
and alert_template.check_email_on_resolved
):
handle_resolved_check_sms_alert_task.delay(self.pk)
if (
not self.resolved_text_sent
and alert_template.check_text_on_resolved
):
handle_resolved_check_sms_alert_task.delay(self.pk)
elif self.fail_count >= self.fails_b4_alert:
if alert_template:
# create alert in dashboard if enabled
if (
self.alert_severity in alert_template.check_dashboard_alert_severity
and self.dashboard_alert
or alert_template.check_always_alert
):
Alert.create_check_alert(self)
# send email if enabled
if (
self.alert_severity in alert_template.check_email_alert_severity
and self.email_alert
or alert_template.check_always_email
):
handle_check_email_alert_task.delay(self.pk)
# send text if enabled
if (
self.alert_severity in alert_template.check_text_alert_severity
and self.text_alert
or alert_template.check_always_text
):
handle_check_sms_alert_task.delay(self.pk)
if alert_template.actions:
# TODO: run scripts on agent
pass
# if alert templates aren't in use
else:
if self.email_alert:
handle_check_email_alert_task.delay(self.pk)
if self.text_alert:
handle_check_sms_alert_task.delay(self.pk)
if self.dashboard_alert:
Alert.create_check_alert(self)
def add_check_history(self, value: int, more_info: Any = None) -> None:
CheckHistory.objects.create(check_history=self, y=value, results=more_info)
def handle_checkv2(self, data):
# alert severity placeholder
alert_severity = None
# cpuload or mem checks
if self.check_type == "cpuload" or self.check_type == "memory":
@@ -271,13 +351,13 @@ class Check(BaseAuditModel):
if self.warning_threshold and avg > self.warning_threshold:
self.status = "failing"
alert_severity = "warning"
self.alert_severity = "warning"
else:
self.status = "passing"
if self.error_threshold and avg > self.error_threshold:
self.status = "failing"
alert_severity = "error"
self.alert_severity = "error"
else:
self.status = "passing"
@@ -296,13 +376,13 @@ class Check(BaseAuditModel):
and (100 - percent_used) < self.warning_threshold
):
self.status = "failing"
alert_severity = "warning"
self.alert_severity = "warning"
else:
self.status = "passing"
if self.error_threshold and (100 - percent_used) < self.error_threshold:
self.status = "failing"
alert_severity = "error"
self.alert_severity = "error"
else:
self.status = "passing"
@@ -312,6 +392,7 @@ class Check(BaseAuditModel):
self.add_check_history(percent_used)
else:
self.status = "failing"
self.alert_severity = "error"
self.more_info = f"Disk {self.disk} does not exist"
self.save(update_fields=["more_info"])
@@ -328,17 +409,15 @@ class Check(BaseAuditModel):
# golang agent
self.execution_time = "{:.4f}".format(data["runtime"])
# check for informational ret codes
if data["retcode"] in self.info_return_codes:
self.status = "passing"
alert_severity = "info"
elif data["retcode"] in self.warning_return_codes:
self.alert_severity = "info"
self.status = "failing"
elif data["retcode"] in self.warning_return_codes:
self.alert_severity = "warning"
self.status = "failing"
alert_severity = "warning"
elif data["retcode"] != 0:
self.status = "failing"
alert_severity = "error"
self.alert_severity = "error"
else:
self.status = "passing"
@@ -382,8 +461,6 @@ class Check(BaseAuditModel):
1 if self.status == "failing" else 0, self.more_info[:60]
)
alert_severity = self.alert_severity
# windows service checks
elif self.check_type == "winsvc":
svc_stat = data["status"]
@@ -427,8 +504,6 @@ class Check(BaseAuditModel):
1 if self.status == "failing" else 0, self.more_info[:60]
)
alert_severity = self.alert_severity
elif self.check_type == "eventlog":
log = []
is_wildcard = self.event_id_is_wildcard
@@ -493,15 +568,12 @@ class Check(BaseAuditModel):
"Events Found:" + str(len(self.extra_details["log"])),
)
alert_severity = self.alert_severity
# handle status
if self.status == "failing":
self.fail_count += 1
self.save(update_fields=["status", "fail_count"])
elif self.status == "passing":
from alerts.models import Alert
if self.fail_count != 0:
self.fail_count = 0
@@ -509,23 +581,7 @@ class Check(BaseAuditModel):
else:
self.save(update_fields=["status"])
# resolve alert if it exists
if Alert.objects.filter(assigned_check=self, resolved=False).exists():
alert = Alert.objects.get(assigned_check=self, resolved=False)
alert.resolve()
# TODO: send email on resolved if enabled
if self.fail_count >= self.fails_b4_alert:
from alerts.models import Alert
# create alert in dashboard
Alert.create_check_alert(self, alert_severity)
if self.email_alert:
handle_check_email_alert_task.delay(self.pk)
if self.text_alert:
handle_check_sms_alert_task.delay(self.pk)
self.handle_alert()
return self.status
@@ -620,6 +676,7 @@ class Check(BaseAuditModel):
CORE = CoreSettings.objects.first()
body: str = ""
if self.agent:
subject = f"{self.agent.client.name}, {self.agent.site.name}, {self} Failed"
else:
@@ -700,6 +757,7 @@ class Check(BaseAuditModel):
def send_sms(self):
CORE = CoreSettings.objects.first()
body: str = ""
if self.agent:
subject = f"{self.agent.client.name}, {self.agent.site.name}, {self} Failed"
@@ -744,6 +802,12 @@ class Check(BaseAuditModel):
CORE.send_sms(body)
def send_resolved_email(self):
pass
def send_resolved_text(self):
pass
class CheckHistory(models.Model):
check_history = models.ForeignKey(

View File

@@ -56,6 +56,40 @@ def handle_check_sms_alert_task(pk):
return "ok"
@app.task
def handle_resolved_check_sms_alert_task(pk):
from .models import Check
check = Check.objects.get(pk=pk)
if not check.agent.maintenance_mode:
# first time sending text
if not check.resolved_text_sent:
sleep(random.randint(1, 3))
check.send_resolved_sms()
check.resolved_text_sent = djangotime.now()
check.save(update_fields=["resolved_text_sent"])
return "ok"
@app.task
def handle_resolved_check_email_alert_task(pk):
from .models import Check
check = Check.objects.get(pk=pk)
if not check.agent.maintenance_mode:
# first time sending email
if not check.resolved_email_sent:
sleep(random.randint(1, 10))
check.send_resolved_email()
check.resolved_email_sent = djangotime.now()
check.save(update_fields=["resolved_email_sent"])
return "ok"
@app.task
def prune_check_history(older_than_days: int) -> str:
from .models import CheckHistory

View File

@@ -68,22 +68,8 @@ class NatsCheckIn(APIView):
action_type="agentupdate", status="pending"
).update(status="completed")
if agent.agentoutages.exists() and agent.agentoutages.last().is_active:
from alerts.models import Alert
last_outage = agent.agentoutages.last()
last_outage.recovery_time = djangotime.now()
last_outage.save(update_fields=["recovery_time"])
# resolve alert if exists
if Alert.objects.filter(agent=agent, resolved=False).exists():
alert = Alert.objects.get(agent=agent, resolved=False)
alert.resolve()
if agent.overdue_email_alert:
agent_recovery_email_task.delay(pk=last_outage.pk)
if agent.overdue_text_alert:
agent_recovery_sms_task.delay(pk=last_outage.pk)
# handles any alerting actions
agent.handle_alert(checkin=True)
recovery = agent.recoveryactions.filter(last_run=None).last()
if recovery is not None:

View File

@@ -45,6 +45,7 @@ export default {
errorColor: "red",
warningColor: "orange",
infoColor: "blue",
poll: null,
};
},
computed: {
@@ -155,5 +156,8 @@ export default {
this.getAlerts();
this.pollAlerts();
},
beforeDestroy() {
clearInterval(this.poll);
},
};
</script>

View File

@@ -9,7 +9,7 @@
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<div class="q-pa-md" style="min-height: 65vh; max-height: 65vh">
<div class="q-pa-sm" style="min-height: 65vh; max-height: 65vh">
<div class="q-gutter-sm">
<q-btn ref="new" label="New" dense flat push unelevated no-caps icon="add" @click="showAddTemplateModal" />
<q-btn

View File

@@ -58,6 +58,10 @@
<template v-slot:header-cell-policystatus="props">
<q-th auto-width :props="props"></q-th>
</template>
<template v-slot:header-cell-status="props">
<q-th auto-width :props="props"></q-th>
</template>
<!-- body slots -->
<template slot="body" slot-scope="props" :props="props">
<q-tr @contextmenu="editTaskPk = props.row.id">
@@ -130,13 +134,37 @@
/>
</q-td>
<!-- policy check icon -->
<q-td v-if="props.row.managed_by_policy">
<q-icon style="font-size: 1.3rem" name="policy">
<q-td>
<q-icon v-if="props.row.managed_by_policy" style="font-size: 1.3rem" name="policy">
<q-tooltip>This task is managed by a policy</q-tooltip>
</q-icon>
</q-td>
<!-- status icon -->
<q-td v-if="props.row.status === 'passing'">
<q-icon style="font-size: 1.3rem" color="positive" name="check_circle">
<q-tooltip>Passing</q-tooltip>
</q-icon>
</q-td>
<q-td v-else-if="props.row.status === 'failing'">
<q-icon v-if="props.row.alert_severity === 'info'" style="font-size: 1.3rem" color="info" name="info">
<q-tooltip>Informational</q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.alert_severity === 'warning'"
style="font-size: 1.3rem"
color="warning"
name="warning"
>
<q-tooltip>Warning</q-tooltip>
</q-icon>
<q-icon v-else style="font-size: 1.3rem" color="negative" name="error">
<q-tooltip>Error</q-tooltip>
</q-icon>
</q-td>
<q-td v-else></q-td>
<!-- name -->
<q-td>{{ props.row.name }}</q-td>
<!-- sync status -->
<q-td v-if="props.row.sync_status === 'notsynced'">Will sync on next agent checkin</q-td>
<q-td v-else-if="props.row.sync_status === 'synced'">Synced with agent</q-td>
<q-td v-else-if="props.row.sync_status === 'pendingdeletion'">Pending deletion on agent</q-td>
@@ -178,8 +206,7 @@
<script>
import axios from "axios";
import { mapState } from "vuex";
import { mapGetters } from "vuex";
import { mapState, mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
import AddAutomatedTask from "@/components/modals/tasks/AddAutomatedTask";
import EditAutomatedTask from "@/components/modals/tasks/EditAutomatedTask";
@@ -203,6 +230,7 @@ export default {
{ name: "emailalert", field: "email_alert", align: "left" },
{ name: "dashboardalert", field: "dashboard_alert", align: "left" },
{ name: "policystatus", align: "left" },
{ name: "status", align: "left" },
{ name: "name", label: "Name", field: "name", align: "left" },
{ name: "sync_status", label: "Sync Status", field: "sync_status", align: "left" },
{

View File

@@ -170,23 +170,30 @@
</q-td>
<q-td v-else></q-td>
<!-- status icon -->
<q-td v-if="props.row.status === 'pending'"></q-td>
<q-td v-else-if="props.row.status === 'passing'">
<q-icon style="font-size: 1.3rem" color="positive" name="check_circle" />
<q-td v-if="props.row.status === 'passing'">
<q-icon style="font-size: 1.3rem" color="positive" name="check_circle">
<q-tooltip>Passing</q-tooltip>
</q-icon>
</q-td>
<q-td v-else-if="props.row.status === 'failing'">
<q-icon style="font-size: 1.3rem" color="negative" name="error" />
<q-icon v-if="props.row.alert_severity === 'info'" style="font-size: 1.3rem" color="info" name="info">
<q-tooltip>Informational</q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.alert_severity === 'warning'"
style="font-size: 1.3rem"
color="warning"
name="warning"
>
<q-tooltip>Warning</q-tooltip>
</q-icon>
<q-icon v-else style="font-size: 1.3rem" color="negative" name="error">
<q-tooltip>Error</q-tooltip>
</q-icon>
</q-td>
<q-td v-else></q-td>
<!-- check description -->
<q-td>{{ props.row.readable_desc }}</q-td>
<!-- status text -->
<q-td v-if="props.row.status === 'pending'">Awaiting First Synchronization</q-td>
<q-td v-else-if="props.row.status === 'passing'">
<q-badge color="positive">Passing</q-badge>
</q-td>
<q-td v-else-if="props.row.status === 'failing'">
<q-badge color="negative">Failing</q-badge>
</q-td>
<!-- more info -->
<q-td>
<span
@@ -274,7 +281,7 @@
<script>
import axios from "axios";
import { mapState, mapGetters } from "vuex";
import { mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
import DiskSpaceCheck from "@/components/modals/checks/DiskSpaceCheck";
import MemCheck from "@/components/modals/checks/MemCheck";
@@ -323,7 +330,6 @@ export default {
{ name: "policystatus", align: "left" },
{ name: "statusicon", align: "left" },
{ name: "desc", field: "readable_desc", label: "Description", align: "left", sortable: true },
{ name: "status", label: "Status", field: "status", align: "left", sortable: true },
{
name: "moreinfo",
label: "More Info",

View File

@@ -9,216 +9,214 @@
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<div class="q-pa-md">
<q-card-section style="min-height: 35vh; max-height: 35vh">
<div class="q-gutter-sm">
<q-btn label="New" dense flat push unelevated no-caps icon="add" @click="showAddPolicyForm" />
<q-btn
label="Edit"
:disable="!selectedPolicy"
dense
flat
push
unelevated
no-caps
icon="edit"
@click="showEditPolicyForm(selectedPolicy)"
/>
<q-btn
label="Delete"
:disable="!selectedPolicy"
dense
flat
push
unelevated
no-caps
icon="delete"
@click="deletePolicy(selectedPolicy)"
/>
<q-btn
label="Policy Overview"
dense
flat
push
unelevated
no-caps
icon="remove_red_eye"
@click="showPolicyOverview"
/>
</div>
<q-table
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
class="tabs-tbl-sticky"
:data="policies"
:columns="columns"
:pagination.sync="pagination"
:rows-per-page-options="[0]"
<q-card-section style="min-height: 35vh; max-height: 35vh">
<div class="q-gutter-sm">
<q-btn label="New" dense flat push unelevated no-caps icon="add" @click="showAddPolicyForm" />
<q-btn
label="Edit"
:disable="!selectedPolicy"
dense
row-key="id"
binary-state-sort
hide-pagination
virtual-scroll
no-data-label="No Policies"
>
<!-- header slots -->
<template v-slot:header-cell-active="props">
<q-th :props="props" auto-width>
<q-icon name="power_settings_new" size="1.5em">
<q-tooltip>Enable Policy</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-enforced="props">
<q-th :props="props" auto-width>
<q-icon name="security" size="1.5em">
<q-tooltip>Enforce Policy (Will override Agent tasks/checks)</q-tooltip>
</q-icon>
</q-th>
</template>
<!-- body slots -->
<template v-slot:body="props">
<q-tr
:props="props"
class="cursor-pointer"
:class="rowSelectedClass(props.row.id, selectedPolicy)"
@click="selectedPolicy = props.row"
@contextmenu="selectedPolicy = props.row"
@dblclick="showEditPolicyForm(props.row)"
>
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showEditPolicyForm(props.row)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showCopyPolicyForm(props.row)">
<q-item-section side>
<q-icon name="content_copy" />
</q-item-section>
<q-item-section>Copy</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="deletePolicy(props.row)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup @click="showRelations(props.row)">
<q-item-section side>
<q-icon name="account_tree" />
</q-item-section>
<q-item-section>Show Relations</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showPatchPolicyForm(props.row)">
<q-item-section side>
<q-icon name="system_update" />
</q-item-section>
<q-item-section>{{ patchPolicyText(props.row) }}</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAlertTemplateAdd(props.row)">
<q-item-section side>
<q-icon name="warning" />
</q-item-section>
<q-item-section>{{ alertTemplateText(props.row) }}</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- enabled checkbox -->
<q-td>
<q-checkbox dense @input="toggleCheckbox(props.row, 'Active')" v-model="props.row.active" />
</q-td>
<!-- enforced checkbox -->
<q-td>
<q-checkbox dense @input="toggleCheckbox(props.row, 'Enforced')" v-model="props.row.enforced" />
</q-td>
<q-td>
{{ props.row.name }}
<q-chip v-if="props.row.default_server_policy" color="primary" text-color="white" size="sm"
>Default Server</q-chip
>
<q-chip v-if="props.row.default_workstation_policy" color="primary" text-color="white" size="sm"
>Default Workstation</q-chip
>
</q-td>
<q-td>{{ props.row.desc }}</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showRelations(props.row)"
>{{ `Show Relations (${props.row.agents_count})` }}</span
>
</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showPatchPolicyForm(props.row)"
>{{ patchPolicyText(props.row) }}</span
>
</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showAlertTemplateAdd(props.row)"
>{{ alertTemplateText(props.row) }}</span
>
</q-td>
<q-td>
<q-icon name="content_copy" size="1.5em" @click="showCopyPolicyForm(props.row)">
<q-tooltip>Create a copy of this policy</q-tooltip>
</q-icon>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
<q-card-section style="min-height: 35vh; max-height: 35vh">
<q-tabs
v-model="subtab"
dense
inline-label
class="text-grey"
active-color="primary"
indicator-color="primary"
align="left"
narrow-indicator
flat
push
unelevated
no-caps
>
<q-tab name="checks" icon="fas fa-check-double" label="Checks" />
<q-tab name="tasks" icon="fas fa-tasks" label="Tasks" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="subtab" :animated="false">
<q-tab-panel name="checks">
<PolicyChecksTab v-if="!!selectedPolicy" :selectedPolicy="selectedPolicy.id" />
</q-tab-panel>
<q-tab-panel name="tasks">
<PolicyAutomatedTasksTab v-if="!!selectedPolicy" :selectedPolicy="selectedPolicy.id" />
</q-tab-panel>
</q-tab-panels>
</q-card-section>
</div>
icon="edit"
@click="showEditPolicyForm(selectedPolicy)"
/>
<q-btn
label="Delete"
:disable="!selectedPolicy"
dense
flat
push
unelevated
no-caps
icon="delete"
@click="deletePolicy(selectedPolicy)"
/>
<q-btn
label="Policy Overview"
dense
flat
push
unelevated
no-caps
icon="remove_red_eye"
@click="showPolicyOverview"
/>
</div>
<q-table
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
class="tabs-tbl-sticky"
:data="policies"
:columns="columns"
:pagination.sync="pagination"
:rows-per-page-options="[0]"
dense
row-key="id"
binary-state-sort
hide-pagination
virtual-scroll
no-data-label="No Policies"
>
<!-- header slots -->
<template v-slot:header-cell-active="props">
<q-th :props="props" auto-width>
<q-icon name="power_settings_new" size="1.5em">
<q-tooltip>Enable Policy</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-enforced="props">
<q-th :props="props" auto-width>
<q-icon name="security" size="1.5em">
<q-tooltip>Enforce Policy (Will override Agent tasks/checks)</q-tooltip>
</q-icon>
</q-th>
</template>
<!-- body slots -->
<template v-slot:body="props">
<q-tr
:props="props"
class="cursor-pointer"
:class="rowSelectedClass(props.row.id, selectedPolicy)"
@click="selectedPolicy = props.row"
@contextmenu="selectedPolicy = props.row"
@dblclick="showEditPolicyForm(props.row)"
>
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showEditPolicyForm(props.row)">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showCopyPolicyForm(props.row)">
<q-item-section side>
<q-icon name="content_copy" />
</q-item-section>
<q-item-section>Copy</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="deletePolicy(props.row)">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup @click="showRelations(props.row)">
<q-item-section side>
<q-icon name="account_tree" />
</q-item-section>
<q-item-section>Show Relations</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showPatchPolicyForm(props.row)">
<q-item-section side>
<q-icon name="system_update" />
</q-item-section>
<q-item-section>{{ patchPolicyText(props.row) }}</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAlertTemplateAdd(props.row)">
<q-item-section side>
<q-icon name="warning" />
</q-item-section>
<q-item-section>{{ alertTemplateText(props.row) }}</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- enabled checkbox -->
<q-td>
<q-checkbox dense @input="toggleCheckbox(props.row, 'Active')" v-model="props.row.active" />
</q-td>
<!-- enforced checkbox -->
<q-td>
<q-checkbox dense @input="toggleCheckbox(props.row, 'Enforced')" v-model="props.row.enforced" />
</q-td>
<q-td>
{{ props.row.name }}
<q-chip v-if="props.row.default_server_policy" color="primary" text-color="white" size="sm"
>Default Server</q-chip
>
<q-chip v-if="props.row.default_workstation_policy" color="primary" text-color="white" size="sm"
>Default Workstation</q-chip
>
</q-td>
<q-td>{{ props.row.desc }}</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showRelations(props.row)"
>{{ `Show Relations (${props.row.agents_count})` }}</span
>
</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showPatchPolicyForm(props.row)"
>{{ patchPolicyText(props.row) }}</span
>
</q-td>
<q-td>
<span
style="cursor: pointer; text-decoration: underline"
class="text-primary"
@click="showAlertTemplateAdd(props.row)"
>{{ alertTemplateText(props.row) }}</span
>
</q-td>
<q-td>
<q-icon name="content_copy" size="1.5em" @click="showCopyPolicyForm(props.row)">
<q-tooltip>Create a copy of this policy</q-tooltip>
</q-icon>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
<q-card-section style="min-height: 35vh; max-height: 35vh">
<q-tabs
v-model="subtab"
dense
inline-label
class="text-grey"
active-color="primary"
indicator-color="primary"
align="left"
narrow-indicator
no-caps
>
<q-tab name="checks" icon="fas fa-check-double" label="Checks" />
<q-tab name="tasks" icon="fas fa-tasks" label="Tasks" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="subtab" :animated="false">
<q-tab-panel name="checks">
<PolicyChecksTab v-if="!!selectedPolicy" :selectedPolicy="selectedPolicy.id" />
</q-tab-panel>
<q-tab-panel name="tasks">
<PolicyAutomatedTasksTab v-if="!!selectedPolicy" :selectedPolicy="selectedPolicy.id" />
</q-tab-panel>
</q-tab-panels>
</q-card-section>
</q-card>
</div>
</q-dialog>

View File

@@ -64,7 +64,7 @@
</template>
<!-- body slots -->
<template v-slot:body="props" :props="props">
<q-tr @dblclick="showEditTask(props.row)">
<q-tr class="cursor-pointer" @dblclick="showEditTask(props.row)">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
@@ -332,7 +332,7 @@ export default {
});
},
},
mounted() {
created() {
this.getTasks();
},
};

View File

@@ -99,7 +99,7 @@
</template>
<!-- body slots -->
<template v-slot:body="props">
<q-tr :props="props" @dblclick="showEditDialog(props.row)">
<q-tr :props="props" class="cursor-pointer" @dblclick="showEditDialog(props.row)">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
@@ -354,7 +354,7 @@ export default {
});
},
},
mounted() {
created() {
this.getChecks();
},
};

View File

@@ -14,7 +14,7 @@
<q-tree
ref="tree"
:nodes="clientSiteTree"
node-key="id"
node-key="key"
selected-color="primary"
:selected.sync="selectedPolicyId"
></q-tree>
@@ -38,10 +38,16 @@
</q-tabs>
<q-tab-panels v-model="selectedTab" animated transition-prev="jump-up" transition-next="jump-up">
<q-tab-panel name="checks">
<PolicyChecksTab v-if="!!selectedPolicyId" :selectedPolicy="selectedPolicyId" />
<PolicyChecksTab
v-if="!!selectedPolicyId"
:selectedPolicy="$refs.tree.getNodeByKey(selectedPolicyId).id"
/>
</q-tab-panel>
<q-tab-panel name="tasks">
<PolicyAutomatedTasksTab v-if="!!selectedPolicyId" :selectedPolicy="selectedPolicyId" />
<PolicyAutomatedTasksTab
v-if="!!selectedPolicyId"
:selectedPolicy="$refs.tree.getNodeByKey(selectedPolicyId).id"
/>
</q-tab-panel>
</q-tab-panels>
</template>
@@ -115,6 +121,7 @@ export default {
client_temp["icon"] = "business";
client_temp["selectable"] = false;
client_temp["children"] = [];
client_temp["key"] = `${unique_id}${client.name}`;
unique_id--;
@@ -127,10 +134,12 @@ export default {
disabled = " (disabled)";
}
const label = client.server_policy.name + " (Servers)" + disabled;
client_temp["children"].push({
label: client.server_policy.name + " (Servers)" + disabled,
label: label,
icon: "policy",
id: client.server_policy.id,
key: `${client.server_policy.id}${label}`,
});
}
@@ -143,10 +152,12 @@ export default {
disabled = " (disabled)";
}
const label = client.workstation_policy.name + " (Workstations)" + disabled;
client_temp["children"].push({
label: client.workstation_policy.name + " (Workstations)" + disabled,
label: label,
icon: "policy",
id: client.workstation_policy.id,
key: `${client.workstation_policy.id}${label}`,
});
}
@@ -157,6 +168,7 @@ export default {
site_temp["id"] = unique_id;
site_temp["icon"] = "apartment";
site_temp["selectable"] = false;
site_temp["key"] = `${unique_id}${site.name}`;
unique_id--;
@@ -170,10 +182,12 @@ export default {
disabled = " (disabled)";
}
const label = site.server_policy.name + " (Servers)" + disabled;
site_temp["children"].push({
label: site.server_policy.name + " (Servers)" + disabled,
label: label,
icon: "policy",
id: site.server_policy.id,
key: `${site.server_policy.id}${label}`,
});
}
@@ -187,10 +201,12 @@ export default {
disabled = " (disabled)";
}
const label = site.workstation_policy.name + " (Workstations)" + disabled;
site_temp["children"].push({
label: site.workstation_policy.name + " (Workstations)" + disabled,
label: label,
icon: "policy",
id: site.workstation_policy.id,
key: `${site.workstation_policy.id}${label}`,
});
}

View File

@@ -42,7 +42,9 @@
<q-item-label v-html="scope.opt.label"></q-item-label>
</q-item-section>
</q-item>
<q-item-label v-if="scope.opt.category" header>{{ scope.opt.category }}</q-item-label>
<q-item-label v-if="scope.opt.category" v-bind="scope.itemProps" header class="q-pa-sm">{{
scope.opt.category
}}</q-item-label>
</template>
</q-select>
</q-card-section>

View File

@@ -112,7 +112,7 @@
<q-separator class="q-mb-sm" />
<q-card-section class="row">
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.agent_email_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Email on resolved<q-tooltip>Sends an email when agent is back online</q-tooltip></span
@@ -120,7 +120,7 @@
</q-toggle>
</div>
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.agent_text_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Text on resolved<q-tooltip>Sends an SMS message when agent is back online</q-tooltip></span
@@ -128,16 +128,6 @@
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.agent_alert_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Dashboard alert on resolved<q-tooltip
>Adds an alert in the dashboard when agent is back online</q-tooltip
></span
>
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.agent_always_email" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
@@ -244,7 +234,7 @@
/>
</div>
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.check_email_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Email on resolved <q-tooltip>Sends an email when check alert has resolved</q-tooltip></span
@@ -252,7 +242,7 @@
</q-toggle>
</div>
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.check_text_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Text on resolved <q-tooltip>Sends an SMS message when check alert has resolved</q-tooltip></span
@@ -260,15 +250,6 @@
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.check_alert_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Dashboard alert on resolved
<q-tooltip>Adds an alert in the dashboard when check alert has resolved</q-tooltip></span
>
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.check_always_email" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
@@ -368,7 +349,7 @@
/>
</div>
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.task_email_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Email on resolved <q-tooltip>Sends an email when task alert has resolved</q-tooltip></span
@@ -376,7 +357,7 @@
</q-toggle>
</div>
<div class="col-4">
<div class="col-6">
<q-toggle v-model="template.task_text_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Text on resolved <q-tooltip>Sends an SMS message when task alert has resolved</q-tooltip></span
@@ -384,15 +365,6 @@
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.task_alert_on_resolved" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
>Dashboard alert on resolved
<q-tooltip>Adds an alert in the dashboard when task alert has resolved</q-tooltip></span
>
</q-toggle>
</div>
<div class="col-4">
<q-toggle v-model="template.task_always_email" color="green" left-label>
<span style="text-decoration: underline; cursor: help"
@@ -458,7 +430,6 @@ export default {
agent_include_desktops: false,
agent_email_on_resolved: false,
agent_text_on_resolved: false,
agent_alert_on_resolved: false,
agent_always_email: false,
agent_always_text: false,
agent_always_alert: false,
@@ -468,7 +439,6 @@ export default {
check_dashboard_alert_severity: [],
check_email_on_resolved: false,
check_text_on_resolved: false,
check_alert_on_resolved: false,
check_always_email: false,
check_always_text: false,
check_always_alert: false,
@@ -478,7 +448,6 @@ export default {
task_dashboard_alert_severity: [],
task_email_on_resolved: false,
task_text_on_resolved: false,
task_alert_on_resolved: false,
task_always_email: false,
task_always_text: false,
task_always_alert: false,

View File

@@ -382,10 +382,10 @@ export default {
},
alertColor(severity) {
if (severity === "error") {
return "negative";
return "red";
}
if (severity === "warning") {
return "warning";
return "orange";
}
if (severity === "info") {
return "info";