diff --git a/apps/audits/api.py b/apps/audits/api.py index ecfc7f1d1..c6c648f5c 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -1,18 +1,18 @@ # -*- coding: utf-8 -*- # -from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin +from django.db.models import F, Value +from django.db.models.functions import Concat -from common.mixins.api import CommonApiMixin from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsOrgAdmin from common.drf.filters import DatetimeRangeFilter, current_user_filter from common.api import CommonGenericViewSet -from orgs.mixins.api import OrgGenericViewSet +from orgs.mixins.api import OrgGenericViewSet, OrgBulkModelViewSet, OrgRelationMixin from orgs.utils import current_org from ops.models import CommandExecution from .models import FTPLog, UserLoginLog, OperateLog, PasswordChangeLog from .serializers import FTPLogSerializer, UserLoginLogSerializer, CommandExecutionSerializer -from .serializers import OperateLogSerializer, PasswordChangeLogSerializer +from .serializers import OperateLogSerializer, PasswordChangeLogSerializer, CommandExecutionHostsRelationSerializer from .filters import CurrentOrgMembersFilter @@ -88,9 +88,31 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): model = CommandExecution serializer_class = CommandExecutionSerializer permission_classes = [IsOrgAdmin | IsOrgAuditor] - extra_filter_backends = [DatetimeRangeFilter, current_user_filter(), CurrentOrgMembersFilter] + extra_filter_backends = [DatetimeRangeFilter, CurrentOrgMembersFilter] date_range_filter_fields = [ ('date_start', ('date_from', 'date_to')) ] + filter_fields = ['user__name', 'command', 'run_as__name'] search_fields = ['command'] ordering = ['-date_created'] + + +class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet): + serializer_class = CommandExecutionHostsRelationSerializer + m2m_field = CommandExecution.hosts.field + permission_classes = (IsOrgAdmin,) + filter_fields = [ + 'id', 'asset', 'commandexecution' + ] + search_fields = ('asset__hostname', ) + http_method_names = ['options', 'get'] + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.annotate( + asset_display=Concat( + F('asset__hostname'), Value('('), + F('asset__ip'), Value(')') + ) + ) + return queryset diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index dbb072d0b..d4213f243 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -2,7 +2,10 @@ # from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from django.db.models import F +from common.mixins import BulkSerializerMixin +from common.serializers import AdaptedBulkListSerializer from terminal.models import Session from ops.models import CommandExecution from . import models @@ -55,12 +58,16 @@ class SessionAuditSerializer(serializers.ModelSerializer): class CommandExecutionSerializer(serializers.ModelSerializer): + is_success = serializers.BooleanField(read_only=True, label=_('Is success')) + class Meta: model = CommandExecution - fields = ( - 'id', 'hosts', 'run_as', 'command', 'user', 'is_finished', + fields_mini = ['id'] + fields_small = fields_mini + [ + 'run_as', 'command', 'user', 'is_finished', 'date_start', 'result', 'is_success' - ) + ] + fields = fields_small + ['hosts', 'run_as_display', 'user_display'] extra_kwargs = { 'result': {'label': _('Result')}, # model 上的方法,只能在这修改 'is_success': {'label': _('Is success')}, @@ -68,3 +75,22 @@ class CommandExecutionSerializer(serializers.ModelSerializer): 'run_as': {'label': _('Run as')}, 'user': {'label': _('User')}, } + + @classmethod + def setup_eager_loading(cls, queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.annotate(user_display=F('user__name'))\ + .annotate(run_as_display=F('run_as__name')) + return queryset + + +class CommandExecutionHostsRelationSerializer(BulkSerializerMixin, serializers.ModelSerializer): + asset_display = serializers.ReadOnlyField() + commandexecution_display = serializers.ReadOnlyField() + + class Meta: + list_serializer_class = AdaptedBulkListSerializer + model = CommandExecution.hosts.through + fields = [ + 'id', 'asset', 'asset_display', 'commandexecution', 'commandexecution_display' + ] diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py index be622a357..8cc822706 100644 --- a/apps/audits/urls/api_urls.py +++ b/apps/audits/urls/api_urls.py @@ -16,6 +16,8 @@ router.register(r'login-logs', api.UserLoginLogViewSet, 'login-log') router.register(r'operate-logs', api.OperateLogViewSet, 'operate-log') router.register(r'password-change-logs', api.PasswordChangeLogViewSet, 'password-change-log') router.register(r'command-execution-logs', api.CommandExecutionViewSet, 'command-execution-log') +router.register(r'command-executions-hosts-relations', api.CommandExecutionHostRelationViewSet, 'command-executions-hosts-relation') + urlpatterns = [ ] diff --git a/apps/common/api.py b/apps/common/api.py index 7ecd77122..56144517e 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -17,7 +17,7 @@ from .utils import get_logger from .mixins import CommonApiMixin __all__ = [ - 'LogTailApi', 'ResourcesIDCacheApi', + 'LogTailApi', 'ResourcesIDCacheApi', 'CommonGenericViewSet' ] logger = get_logger(__file__) diff --git a/apps/ops/models/command.py b/apps/ops/models/command.py index fb6642f85..408c72915 100644 --- a/apps/ops/models/command.py +++ b/apps/ops/models/command.py @@ -9,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from django.db import models - +from common.utils import lazyproperty from orgs.models import Organization from orgs.mixins.models import OrgModelMixin from ..ansible.runner import CommandRunner @@ -40,6 +40,14 @@ class CommandExecution(OrgModelMixin): inv = JMSInventory(self.hosts.all(), run_as=username) return inv + @lazyproperty + def run_as_display(self): + return str(self.run_as) + + @lazyproperty + def user_display(self): + return str(self.user) + @property def result(self): if self._result: diff --git a/apps/orgs/mixins/api.py b/apps/orgs/mixins/api.py index 2ad34831c..635e415bf 100644 --- a/apps/orgs/mixins/api.py +++ b/apps/orgs/mixins/api.py @@ -6,12 +6,12 @@ from rest_framework_bulk import BulkModelViewSet from common.mixins import CommonApiMixin, RelationMixin from orgs.utils import current_org -from ..utils import set_to_root_org, filter_org_queryset +from ..utils import set_to_root_org from ..models import Organization __all__ = [ 'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet', - 'OrgBulkModelViewSet', 'OrgQuerySetMixin', + 'OrgBulkModelViewSet', 'OrgQuerySetMixin', 'OrgGenericViewSet', 'OrgRelationMixin' ]