From e82eb8f3d1fe30c7f93398a3fd6df5e4526c07f9 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 19 Dec 2022 18:04:11 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=89=B9=E9=87=8F=E5=91=BD=E4=BB=A4=20?= =?UTF-8?q?(#9220)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/api.py | 23 +++++++++------------ apps/jumpserver/api.py | 46 ++++++++++++++++++++++++++++++++++++++++++ apps/ops/const.py | 22 ++++++++++++++++++++ apps/ops/models/job.py | 24 ++++++---------------- 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index ba556d452..990d4353a 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -2,34 +2,29 @@ # from importlib import import_module -from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin -from django.db.models import F, Value -from django.db.models.functions import Concat from django.conf import settings -from rest_framework.permissions import IsAuthenticated from rest_framework import generics +from rest_framework.permissions import IsAuthenticated +from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin -from common.drf.api import JMSReadOnlyModelViewSet -from common.plugins.es import QuerySet as ESQuerySet -from common.drf.filters import DatetimeRangeFilter -from common.api import CommonGenericViewSet from ops.models.job import JobAuditLog -from orgs.mixins.api import OrgGenericViewSet, OrgBulkModelViewSet +from common.api import CommonGenericViewSet +from common.drf.filters import DatetimeRangeFilter +from common.plugins.es import QuerySet as ESQuerySet from orgs.utils import current_org +from orgs.mixins.api import OrgGenericViewSet, OrgBulkModelViewSet from .backends import TYPE_ENGINE_MAPPING from .models import FTPLog, UserLoginLog, OperateLog, PasswordChangeLog from .serializers import FTPLogSerializer, UserLoginLogSerializer, JobAuditLogSerializer from .serializers import ( - OperateLogSerializer, OperateLogActionDetailSerializer, - PasswordChangeLogSerializer + OperateLogSerializer, OperateLogActionDetailSerializer, PasswordChangeLogSerializer ) class JobAuditViewSet(OrgBulkModelViewSet): - serializer_class = JobAuditLogSerializer - http_method_names = ('get', 'head', 'options',) - permission_classes = () model = JobAuditLog + serializer_class = JobAuditLogSerializer + http_method_names = ('get', 'head', 'options') class FTPLogViewSet(CreateModelMixin, ListModelMixin, OrgGenericViewSet): diff --git a/apps/jumpserver/api.py b/apps/jumpserver/api.py index 284ad429d..565ffbacb 100644 --- a/apps/jumpserver/api.py +++ b/apps/jumpserver/api.py @@ -16,6 +16,8 @@ from assets.const import AllTypes from terminal.models import Session, Command from terminal.utils import ComponentsPrometheusMetricsUtil from orgs.utils import current_org +from ops.const import JobStatus +from ops.models import Job, JobExecution from common.utils import lazyproperty from audits.models import UserLoginLog, PasswordChangeLog, OperateLog from audits.const import LoginStatusChoices @@ -118,12 +120,26 @@ class DateTimeMixin: queryset = Command.objects.filter(timestamp__gte=t) return queryset + @lazyproperty + def jobs_queryset(self): + t = self.days_to_datetime + queryset = Job.objects.filter(date_created__gte=t) + return queryset + + @lazyproperty + def jobs_executed_queryset(self): + t = self.days_to_datetime + queryset = JobExecution.objects.filter(date_created__gte=t) + return queryset + class DatesLoginMetricMixin: dates_list: list command_queryset: Command.objects sessions_queryset: Session.objects ftp_logs_queryset: OperateLog.objects + jobs_queryset: Job.objects + jobs_executed_queryset: JobExecution.objects login_logs_queryset: UserLoginLog.objects operate_logs_queryset: OperateLog.objects password_change_logs_queryset: PasswordChangeLog.objects @@ -299,6 +315,21 @@ class DatesLoginMetricMixin: def commands_danger_amount(self): return self.command_queryset.filter(risk_level=Command.RISK_LEVEL_DANGEROUS).count() + @lazyproperty + def jobs_amount(self): + return self.jobs_queryset.count() + + @lazyproperty + def jobs_unexecuted_amount(self): + executed_amount = self.jobs_executed_queryset.values( + 'job_id').order_by('job_id').distinct().count() + return self.jobs_amount - executed_amount + + @lazyproperty + def jobs_executed_failed_amount(self): + return self.jobs_executed_queryset.objects.filter( + status=JobStatus.failed).count() + @lazyproperty def sessions_amount(self): return self.sessions_queryset.count() @@ -408,6 +439,21 @@ class IndexApi(DateTimeMixin, DatesLoginMetricMixin, APIView): 'total_count_ftp_logs': self.ftp_logs_amount, }) + if _all or query_params.get('total_count') or query_params.get('total_count_jobs'): + data.update({ + 'total_count_jobs': self.jobs_amount, + }) + + if _all or query_params.get('total_count') or query_params.get('total_count_jobs_unexecuted'): + data.update({ + 'total_count_jobs_unexecuted': self.jobs_unexecuted_amount, + }) + + if _all or query_params.get('total_count') or query_params.get('total_count_jobs_executed_failed'): + data.update({ + 'total_count_jobs_executed_failed': self.jobs_executed_failed_amount, + }) + if _all or query_params.get('total_count') or query_params.get('total_count_type_to_assets_amount'): data.update({ 'total_count_type_to_assets_amount': self.get_type_to_assets, diff --git a/apps/ops/const.py b/apps/ops/const.py index 2f68efd3b..8288a663e 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -27,3 +27,25 @@ DEFAULT_PASSWORD_RULES = { 'length': DEFAULT_PASSWORD_LENGTH, 'symbol_set': string_punctuation } + + +class Types(models.TextChoices): + adhoc = 'adhoc', _('Adhoc') + playbook = 'playbook', _('Playbook') + + +class RunasPolicies(models.TextChoices): + privileged_only = 'privileged_only', _('Privileged Only') + privileged_first = 'privileged_first', _('Privileged First') + skip = 'skip', _('Skip') + + +class Modules(models.TextChoices): + shell = 'shell', _('Shell') + winshell = 'win_shell', _('Powershell') + + +class JobStatus(models.TextChoices): + running = 'running', _('Running') + success = 'success', _('Success') + failed = 'failed', _('Failed') diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index b63628b47..d9a4bdc58 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -14,23 +14,11 @@ __all__ = ["Job", "JobExecution", "JobAuditLog"] from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner from ops.mixin import PeriodTaskModelMixin from ops.variables import * +from ops.const import Types, Modules, RunasPolicies, JobStatus from orgs.mixins.models import JMSOrgBaseModel class Job(JMSOrgBaseModel, PeriodTaskModelMixin): - class Types(models.TextChoices): - adhoc = 'adhoc', _('Adhoc') - playbook = 'playbook', _('Playbook') - - class RunasPolicies(models.TextChoices): - privileged_only = 'privileged_only', _('Privileged Only') - privileged_first = 'privileged_first', _('Privileged First') - skip = 'skip', _('Skip') - - class Modules(models.TextChoices): - shell = 'shell', _('Shell') - winshell = 'win_shell', _('Powershell') - id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, null=True, verbose_name=_('Name')) instant = models.BooleanField(default=False) @@ -100,7 +88,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin): class JobExecution(JMSOrgBaseModel): id = models.UUIDField(default=uuid.uuid4, primary_key=True) task_id = models.UUIDField(null=True) - status = models.CharField(max_length=16, verbose_name=_('Status'), default='running') + status = models.CharField(max_length=16, verbose_name=_('Status'), default=JobStatus.running) job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='executions', null=True) parameters = models.JSONField(default=dict, verbose_name=_('Parameters')) result = models.JSONField(blank=True, null=True, verbose_name=_('Result')) @@ -226,11 +214,11 @@ class JobExecution(JMSOrgBaseModel): @property def is_finished(self): - return self.status in ['success', 'failed'] + return self.status in [JobStatus.success, JobStatus.failed] @property def is_success(self): - return self.status == 'success' + return self.status == JobStatus.success @property def inventory_path(self): @@ -244,13 +232,13 @@ class JobExecution(JMSOrgBaseModel): def set_error(self, error): this = self.__class__.objects.get(id=self.id) # 重新获取一次,避免数据库超时连接超时 - this.status = 'failed' + this.status = JobStatus.failed this.summary.update({'error': str(error)}) this.finish_task() def set_result(self, cb): status_mapper = { - 'successful': 'success', + 'successful': JobStatus.success, } this = self.__class__.objects.get(id=self.id) this.status = status_mapper.get(cb.status, cb.status)