Files
tacticalrmm/api/tacticalrmm/autotasks/serializers.py
wh1te909 9f2f23fa96 blacked
2023-02-15 00:21:57 +00:00

281 lines
10 KiB
Python

from datetime import datetime
from django.utils import timezone as djangotime
from rest_framework import serializers
from scripts.models import Script
from tacticalrmm.constants import TaskType
from .models import AutomatedTask, TaskResult
class TaskResultSerializer(serializers.ModelSerializer):
class Meta:
model = TaskResult
fields = "__all__"
read_only_fields = ("agent", "task")
class TaskSerializer(serializers.ModelSerializer):
check_name = serializers.ReadOnlyField(source="assigned_check.readable_desc")
schedule = serializers.ReadOnlyField()
alert_template = serializers.SerializerMethodField()
run_time_date = serializers.DateTimeField(required=False)
expire_date = serializers.DateTimeField(allow_null=True, required=False)
task_result = serializers.SerializerMethodField()
def get_task_result(self, obj):
return (
TaskResultSerializer(obj.task_result).data
if isinstance(obj.task_result, TaskResult)
else {}
)
def validate_actions(self, value):
if not value:
raise serializers.ValidationError(
"There must be at least one action configured"
)
for action in value:
if "type" not in action:
raise serializers.ValidationError(
"Each action must have a type field of either 'script' or 'cmd'"
)
if action["type"] == "script":
if "script" not in action:
raise serializers.ValidationError(
"A script action type must have a 'script' field with primary key of script"
)
if "script_args" not in action:
raise serializers.ValidationError(
"A script action type must have a 'script_args' field with an array of arguments"
)
if "timeout" not in action:
raise serializers.ValidationError(
"A script action type must have a 'timeout' field"
)
if action["type"] == "cmd":
if "command" not in action:
raise serializers.ValidationError(
"A command action type must have a 'command' field"
)
if "timeout" not in action:
raise serializers.ValidationError(
"A command action type must have a 'timeout' field"
)
return value
def validate(self, data):
# allow editing with task_type not specified
if self.instance and "task_type" not in data:
# remove schedule related fields from data
if "run_time_date" in data:
del data["run_time_date"]
if "expire_date" in data:
del data["expire_date"]
if "daily_interval" in data:
del data["daily_interval"]
if "weekly_interval" in data:
del data["weekly_interval"]
if "run_time_bit_weekdays" in data:
del data["run_time_bit_weekdays"]
if "monthly_months_of_year" in data:
del data["monthly_months_of_year"]
if "monthly_days_of_month" in data:
del data["monthly_days_of_month"]
if "monthly_weeks_of_month" in data:
del data["monthly_weeks_of_month"]
if "assigned_check" in data:
del data["assigned_check"]
return data
if (
"expire_date" in data
and isinstance(data["expire_date"], datetime)
and djangotime.now() > data["expire_date"]
):
raise serializers.ValidationError("Expires date/time is in the past")
# run_time_date required
if (
data["task_type"]
in (
TaskType.RUN_ONCE,
TaskType.DAILY,
TaskType.WEEKLY,
TaskType.MONTHLY,
TaskType.MONTHLY_DOW,
)
and not data["run_time_date"]
):
raise serializers.ValidationError(
f"run_time_date is required for task_type '{data['task_type']}'"
)
# daily task type validation
if data["task_type"] == TaskType.DAILY:
if "daily_interval" not in data or not data["daily_interval"]:
raise serializers.ValidationError(
f"daily_interval is required for task_type '{data['task_type']}'"
)
# weekly task type validation
elif data["task_type"] == TaskType.WEEKLY:
if "weekly_interval" not in data or not data["weekly_interval"]:
raise serializers.ValidationError(
f"weekly_interval is required for task_type '{data['task_type']}'"
)
if "run_time_bit_weekdays" not in data or not data["run_time_bit_weekdays"]:
raise serializers.ValidationError(
f"run_time_bit_weekdays is required for task_type '{data['task_type']}'"
)
# monthly task type validation
elif data["task_type"] == TaskType.MONTHLY:
if (
"monthly_months_of_year" not in data
or not data["monthly_months_of_year"]
):
raise serializers.ValidationError(
f"monthly_months_of_year is required for task_type '{data['task_type']}'"
)
if "monthly_days_of_month" not in data or not data["monthly_days_of_month"]:
raise serializers.ValidationError(
f"monthly_days_of_month is required for task_type '{data['task_type']}'"
)
# monthly day of week task type validation
elif data["task_type"] == TaskType.MONTHLY_DOW:
if (
"monthly_months_of_year" not in data
or not data["monthly_months_of_year"]
):
raise serializers.ValidationError(
f"monthly_months_of_year is required for task_type '{data['task_type']}'"
)
if (
"monthly_weeks_of_month" not in data
or not data["monthly_weeks_of_month"]
):
raise serializers.ValidationError(
f"monthly_weeks_of_month is required for task_type '{data['task_type']}'"
)
if "run_time_bit_weekdays" not in data or not data["run_time_bit_weekdays"]:
raise serializers.ValidationError(
f"run_time_bit_weekdays is required for task_type '{data['task_type']}'"
)
# check failure task type validation
elif data["task_type"] == TaskType.CHECK_FAILURE:
if "assigned_check" not in data or not data["assigned_check"]:
raise serializers.ValidationError(
f"assigned_check is required for task_type '{data['task_type']}'"
)
return data
def get_alert_template(self, obj):
if obj.agent:
alert_template = obj.agent.alert_template
else:
alert_template = None
if not alert_template:
return None
return {
"name": alert_template.name,
"always_email": alert_template.task_always_email,
"always_text": alert_template.task_always_text,
"always_alert": alert_template.task_always_alert,
}
class Meta:
model = AutomatedTask
fields = "__all__"
class TaskGOGetSerializer(serializers.ModelSerializer):
task_actions = serializers.SerializerMethodField()
def get_task_actions(self, obj):
tmp = []
actions_to_remove = []
agent = self.context["agent"]
for action in obj.actions:
if action["type"] == "cmd":
tmp.append(
{
"type": "cmd",
"command": Script.parse_script_args(
agent=agent,
shell=action["shell"],
args=[action["command"]],
)[0],
"shell": action["shell"],
"timeout": action["timeout"],
}
)
elif action["type"] == "script":
try:
script = Script.objects.get(pk=action["script"])
except Script.DoesNotExist:
# script doesn't exist so remove it
actions_to_remove.append(action["script"])
continue
# wrote a custom migration for env_vars but leaving this just in case.
# can be removed later
try:
env_vars = action["env_vars"]
except KeyError:
env_vars = []
tmp.append(
{
"type": "script",
"script_name": script.name,
"code": script.code,
"script_args": Script.parse_script_args(
agent=agent,
shell=script.shell,
args=action["script_args"],
),
"shell": script.shell,
"timeout": action["timeout"],
"run_as_user": script.run_as_user,
"env_vars": env_vars,
}
)
if actions_to_remove:
task = AutomatedTask.objects.get(pk=obj.pk)
task.actions = [
action
for action in task.actions
if action["type"] == "cmd"
or (
"script" in action.keys()
and action["script"] not in actions_to_remove
)
]
task.save(update_fields=["actions"])
return tmp
class Meta:
model = AutomatedTask
fields = ["id", "continue_on_error", "enabled", "task_actions"]
class TaskAuditSerializer(serializers.ModelSerializer):
class Meta:
model = AutomatedTask
fields = "__all__"