perf: 批量命令 (#9220)

Co-authored-by: feng <1304903146@qq.com>
pull/9221/head
fit2bot 2022-12-19 18:04:11 +08:00 committed by GitHub
parent 92a198c00b
commit e82eb8f3d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 32 deletions

View File

@ -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):

View File

@ -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,

View File

@ -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')

View File

@ -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)