From c0de27ff7a3a4c6fd25bb371d05d98538c7d5756 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:11:51 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=B5=84=E4=BA=A7=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B9=B3=E5=8F=B0=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=B9=B3=E5=8F=B0=E7=BA=A6=E6=9D=9F=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E8=87=AA=E5=8A=A8=E7=94=9F=E6=95=88=20(#11818)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/api/account/template.py | 2 +- apps/assets/api/asset/asset.py | 39 ++++++++++++++++++++++++- apps/assets/serializers/asset/common.py | 27 +++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index 4493cb227..22d9508a7 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -46,7 +46,7 @@ class AccountTemplateViewSet(OrgBulkModelViewSet): } rbac_perms = { 'su_from_account_templates': 'accounts.view_accounttemplate', - 'sync_related_accounts': 'accounts.change_accounttemplate', + 'sync_related_accounts': 'accounts.change_account', } @action(methods=['get'], detail=False, url_path='su-from-account-templates') diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index f1573629e..4b36b4bbe 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- # +from collections import defaultdict + import django_filters from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.status import HTTP_200_OK @@ -12,7 +15,7 @@ from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connect from assets import serializers from assets.exceptions import NotSupportedTemporarilyError from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend -from assets.models import Asset, Gateway, Platform +from assets.models import Asset, Gateway, Platform, Protocol from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin from common.drf.filters import BaseFilterSet, AttrRulesFilterBackend @@ -115,6 +118,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ("gateways", "assets.view_gateway"), ("spec_info", "assets.view_asset"), ("gathered_info", "assets.view_asset"), + ("sync_platform_protocols", "assets.change_asset"), ) extra_filter_backends = [ LabelFilterBackend, IpInFilterBackend, @@ -152,6 +156,39 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): gateways = asset.domain.gateways return self.get_paginated_response_from_queryset(gateways) + @action(methods=['post'], detail=False, url_path='sync-platform-protocols') + def sync_platform_protocols(self, request, *args, **kwargs): + platform_id = request.data.get('platform_id') + platform = get_object_or_404(Platform, pk=platform_id) + assets = platform.assets.all() + + platform_protocols = { + p['name']: p['port'] + for p in platform.protocols.values('name', 'port') + } + asset_protocols_map = defaultdict(set) + protocols = assets.prefetch_related('protocols').values_list( + 'id', 'protocols__name' + ) + for asset_id, protocol in protocols: + asset_id = str(asset_id) + asset_protocols_map[asset_id].add(protocol) + objs = [] + for asset_id, protocols in asset_protocols_map.items(): + protocol_names = set(platform_protocols) - protocols + if not protocol_names: + continue + for name in protocol_names: + objs.append( + Protocol( + name=name, + port=platform_protocols[name], + asset_id=asset_id, + ) + ) + Protocol.objects.bulk_create(objs) + return Response(status=status.HTTP_200_OK) + def create(self, request, *args, **kwargs): if request.path.find('/api/v1/assets/assets/') > -1: error = _('Cannot create asset directly, you should create a host or other') diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index ea2be442c..6d73287ee 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -298,10 +298,37 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali self.perform_nodes_display_create(instance, nodes_display) return instance + @staticmethod + def sync_platform_protocols(instance, old_platform): + platform = instance.platform + + if str(old_platform.id) == str(instance.platform_id): + return + + platform_protocols = { + p['name']: p['port'] + for p in platform.protocols.values('name', 'port') + } + + protocols = set(instance.protocols.values_list('name', flat=True)) + protocol_names = set(platform_protocols) - protocols + objs = [] + for name in protocol_names: + objs.append( + Protocol( + name=name, + port=platform_protocols[name], + asset_id=instance.id, + ) + ) + Protocol.objects.bulk_create(objs) + @atomic def update(self, instance, validated_data): + old_platform = instance.platform nodes_display = validated_data.pop('nodes_display', '') instance = super().update(instance, validated_data) + self.sync_platform_protocols(instance, old_platform) self.perform_nodes_display_create(instance, nodes_display) return instance