perf: 修改 platform

pull/8873/head
ibuler 2022-08-18 17:58:59 +08:00
parent a9bf4eddea
commit 05f913ab18
10 changed files with 45 additions and 173 deletions

View File

@ -29,7 +29,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
filterset_fields = { filterset_fields = {
'name': ['exact'], 'name': ['exact'],
'ip': ['exact'], 'ip': ['exact'],
'system_users__id': ['exact'],
'is_active': ['exact'], 'is_active': ['exact'],
'protocols': ['exact', 'icontains'] 'protocols': ['exact', 'icontains']
} }

View File

@ -18,9 +18,6 @@ def create_app_platform(apps, *args):
{'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'}, {'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'},
{'name': 'Redis', 'category': 'database', 'type': 'redis'}, {'name': 'Redis', 'category': 'database', 'type': 'redis'},
{'name': 'Chrome', 'category': 'remote_app', 'type': 'chrome'}, {'name': 'Chrome', 'category': 'remote_app', 'type': 'chrome'},
{'name': 'vSphereClient', 'category': 'remote_app', 'type': 'vmware_client'},
{'name': 'MySQLWorkbench', 'category': 'remote_app', 'type': 'mysql_workbench'},
{'name': 'GeneralRemoteApp', 'category': 'remote_app', 'type': 'general_remote_app'},
{'name': 'Kubernetes', 'category': 'cloud', 'type': 'k8s'}, {'name': 'Kubernetes', 'category': 'cloud', 'type': 'k8s'},
] ]

View File

@ -11,6 +11,15 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.CreateModel(
name='PlatformProtocol',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=32, verbose_name='Name')),
('port', models.IntegerField(verbose_name='Port')),
('setting', models.JSONField(default=dict, verbose_name='Setting')),
],
),
migrations.AddField( migrations.AddField(
model_name='platform', model_name='platform',
name='domain_default', name='domain_default',
@ -28,8 +37,8 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='platform', model_name='platform',
name='protocols_enabled', name='protocols',
field=models.BooleanField(default=True, verbose_name='Protocols enabled'), field=models.ManyToManyField(blank=True, to='assets.PlatformProtocol', verbose_name='Protocols'),
), ),
migrations.AddField( migrations.AddField(
model_name='platform', model_name='platform',

View File

@ -1,137 +0,0 @@
# -*- coding: utf-8 -*-
#
from django.db import models
from django.db.models import F
from django.utils.translation import ugettext_lazy as _
from simple_history.models import HistoricalRecords
from common.utils import lazyproperty, get_logger
from .base import BaseAccount, AbsConnectivity
logger = get_logger(__name__)
__all__ = ['AuthBook']
class AuthBook(BaseAccount, AbsConnectivity):
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user"))
version = models.IntegerField(default=1, verbose_name=_('Version'))
history = HistoricalRecords()
auth_attrs = ['username', 'password', 'private_key', 'public_key']
class Meta:
verbose_name = _('AuthBook')
unique_together = [('username', 'asset', 'systemuser')]
permissions = [
('test_authbook', _('Can test asset account connectivity')),
('view_assetaccountsecret', _('Can view asset account secret')),
('change_assetaccountsecret', _('Can change asset account secret')),
('view_assethistoryaccount', _('Can view asset history account')),
('view_assethistoryaccountsecret', _('Can view asset history account secret')),
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.auth_snapshot = {}
def get_or_systemuser_attr(self, attr):
val = getattr(self, attr, None)
if val:
return val
if self.systemuser:
return getattr(self.systemuser, attr, '')
return ''
def load_auth(self):
for attr in self.auth_attrs:
value = self.get_or_systemuser_attr(attr)
self.auth_snapshot[attr] = [getattr(self, attr), value]
setattr(self, attr, value)
def unload_auth(self):
if not self.systemuser:
return
for attr, values in self.auth_snapshot.items():
origin_value, loaded_value = values
current_value = getattr(self, attr, '')
if current_value == loaded_value:
setattr(self, attr, origin_value)
def save(self, *args, **kwargs):
self.unload_auth()
instance = super().save(*args, **kwargs)
self.load_auth()
return instance
@property
def username_display(self):
return self.get_or_systemuser_attr('username') or '*'
@lazyproperty
def systemuser_display(self):
if not self.systemuser:
return ''
return str(self.systemuser)
@property
def smart_name(self):
username = self.username_display
if self.asset:
asset = str(self.asset)
else:
asset = '*'
return '{}@{}'.format(username, asset)
def sync_to_system_user_account(self):
if self.systemuser:
return
matched = AuthBook.objects.filter(
asset=self.asset, systemuser__username=self.username
)
if not matched:
return
for i in matched:
i.password = self.password
i.private_key = self.private_key
i.public_key = self.public_key
i.comment = 'Update triggered by account {}'.format(self.id)
# 不触发post_save信号
self.__class__.objects.bulk_update(matched, fields=['password', 'private_key', 'public_key'])
def remove_asset_admin_user_if_need(self):
if not self.asset or not self.systemuser:
return
if not self.systemuser.is_admin_user or self.asset.admin_user != self.systemuser:
return
self.asset.admin_user = None
self.asset.save()
logger.debug('Remove asset admin user: {} {}'.format(self.asset, self.systemuser))
def update_asset_admin_user_if_need(self):
if not self.asset or not self.systemuser:
return
if not self.systemuser.is_admin_user or self.asset.admin_user == self.systemuser:
return
self.asset.admin_user = self.systemuser
self.asset.save()
logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
@classmethod
def get_queryset(cls):
queryset = cls.objects.all() \
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname')) \
.annotate(platform=F('asset__platform__name')) \
.annotate(protocols=F('asset__protocols'))
return queryset
def __str__(self):
return self.smart_name

View File

@ -157,13 +157,6 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
tree_node = TreeNode(**data) tree_node = TreeNode(**data)
return tree_node return tree_node
def get_all_system_users(self):
from assets.models import SystemUser
system_user_ids = SystemUser.assets.through.objects.filter(asset=self) \
.values_list('systemuser_id', flat=True)
system_users = SystemUser.objects.filter(id__in=system_user_ids)
return system_users
class Meta: class Meta:
unique_together = [('org_id', 'name')] unique_together = [('org_id', 'name')]
verbose_name = _("Asset") verbose_name = _("Asset")

View File

@ -5,7 +5,13 @@ from assets.const import Category, AllTypes
from common.db.fields import JsonDictTextField from common.db.fields import JsonDictTextField
__all__ = ['Platform'] __all__ = ['Platform', 'PlatformProtocol']
class PlatformProtocol(models.Model):
name = models.CharField(max_length=32, verbose_name=_('Name'))
port = models.IntegerField(verbose_name=_('Port'))
setting = models.JSONField(verbose_name=_('Setting'), default=dict)
class Platform(models.Model): class Platform(models.Model):
@ -30,9 +36,7 @@ class Platform(models.Model):
verbose_name=_("Domain default") verbose_name=_("Domain default")
) )
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled")) protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
protocols_default = models.JSONField( protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
max_length=128, default=list, blank=True, verbose_name=_("Protocols default")
)
# Accounts # Accounts
# 这应该和账号有关 # 这应该和账号有关
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled")) su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))

View File

@ -4,8 +4,10 @@ from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.drf.serializers import JMSWritableNestedModelSerializer from common.drf.serializers import JMSWritableNestedModelSerializer
from common.drf.fields import ChoiceDisplayField
from ..account import AccountSerializer from ..account import AccountSerializer
from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account
from ...const import Category, AllTypes
__all__ = [ __all__ = [
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
@ -57,6 +59,8 @@ class AssetNodesSerializer(serializers.ModelSerializer):
class AssetSerializer(JMSWritableNestedModelSerializer): class AssetSerializer(JMSWritableNestedModelSerializer):
category = ChoiceDisplayField(choices=Category.choices, read_only=True, label=_('Category'))
type = ChoiceDisplayField(choices=AllTypes.choices, read_only=True, label=_('Type'))
domain = AssetDomainSerializer(required=False) domain = AssetDomainSerializer(required=False)
platform = AssetPlatformSerializer(required=False) platform = AssetPlatformSerializer(required=False)
labels = AssetLabelSerializer(many=True, required=False) labels = AssetLabelSerializer(many=True, required=False)

View File

@ -1,42 +1,43 @@
from rest_framework import serializers from rest_framework import serializers
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from assets.models import Platform from common.drf.fields import ChoiceDisplayField
from .mixin import CategoryDisplayMixin from common.drf.serializers import JMSWritableNestedModelSerializer
from ..models import Platform, PlatformProtocol
from ..const import Category, AllTypes
__all__ = ['PlatformSerializer'] __all__ = ['PlatformSerializer']
class PlatformProtocolsSerializer(serializers.Serializer): class PlatformProtocolsSerializer(serializers.ModelSerializer):
name = serializers.CharField(max_length=255, required=True) class Meta:
port = serializers.IntegerField(max_value=65535, min_value=1, required=True) model = PlatformProtocol
fields = ['id', 'name', 'port', 'setting']
class PlatformSerializer(CategoryDisplayMixin, serializers.ModelSerializer): class PlatformSerializer(JMSWritableNestedModelSerializer):
meta = serializers.DictField(required=False, allow_null=True, label=_('Meta')) type = ChoiceDisplayField(choices=AllTypes.choices, label=_("Type"))
protocols_default = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False) category = ChoiceDisplayField(choices=Category.choices, label=_("Category"))
protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
type_constraints = serializers.ReadOnlyField(required=False, read_only=True) type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
class Meta: class Meta:
model = Platform model = Platform
fields_mini = ['id', 'name', 'internal'] fields_mini = ['id', 'name', 'internal']
fields_small = fields_mini + [ fields_small = fields_mini + [
'meta', 'comment', 'charset', 'category', 'type',
'category', 'category_display', ]
'type', 'type_display', fields = fields_small + [
'domain_enabled', 'domain_default',
'su_enabled', 'su_method', 'su_enabled', 'su_method',
'protocols_enabled', 'protocols',
'ping_enabled', 'ping_method', 'ping_enabled', 'ping_method',
'verify_account_enabled', 'verify_account_method', 'verify_account_enabled', 'verify_account_method',
'create_account_enabled', 'create_account_method', 'create_account_enabled', 'create_account_method',
'change_password_enabled', 'change_password_method', 'change_password_enabled', 'change_password_method',
'type_constraints', 'type_constraints',
'comment', 'charset',
] ]
fields_fk = [
'domain_enabled', 'domain_default',
'protocols_enabled', 'protocols_default',
]
fields = fields_small + fields_fk
read_only_fields = [ read_only_fields = [
'category_display', 'type_display', 'category_display', 'type_display',
] ]

View File

@ -51,6 +51,6 @@ class ChoiceDisplayField(ChoiceField):
if value in ('', None): if value in ('', None):
return value return value
return { return {
'name': value, 'value': value,
'label': self.choice_mapper.get(six.text_type(value), value), 'label': self.choice_mapper.get(six.text_type(value), value),
} }

View File

@ -28,7 +28,9 @@ class CommandExecutionViewSet(RootOrgViewMixin, viewsets.ModelViewSet):
system_user = data["run_as"] system_user = data["run_as"]
user = self.request.user user = self.request.user
q = Q(granted_by_permissions__system_users__id=system_user.id) & ( # TOdo:
# Q(granted_by_permissions__system_users__id=system_user.id) &
q = (
Q(granted_by_permissions__users=user) | Q(granted_by_permissions__users=user) |
Q(granted_by_permissions__user_groups__users=user) Q(granted_by_permissions__user_groups__users=user)
) )