add handle alerts functions to agents, checks, and tasks. Minor fixes
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
25
api/tacticalrmm/alerts/migrations/0015_auto_20210205_1647.py
Normal file
25
api/tacticalrmm/alerts/migrations/0015_auto_20210205_1647.py
Normal 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',
|
||||
),
|
||||
]
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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()
|
||||
|
||||
18
api/tacticalrmm/checks/migrations/0018_auto_20210205_1647.py
Normal file
18
api/tacticalrmm/checks/migrations/0018_auto_20210205_1647.py
Normal 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),
|
||||
),
|
||||
]
|
||||
23
api/tacticalrmm/checks/migrations/0019_auto_20210205_1728.py
Normal file
23
api/tacticalrmm/checks/migrations/0019_auto_20210205_1728.py
Normal 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),
|
||||
),
|
||||
]
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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" },
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user