mirror of https://github.com/jumpserver/jumpserver
perf: 修改 model
parent
4947b0d8fd
commit
d7d9fe2718
|
@ -77,7 +77,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='platform',
|
||||
name='su_method',
|
||||
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='SU method'),
|
||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='SU method'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='platform',
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 3.2.14 on 2022-09-01 02:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0107_alter_accountbackupplan_types'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='create_account_enabled',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='create_account_method',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='domain_default',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='domain_enabled',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='ping_enabled',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='platform',
|
||||
name='ping_method',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 3.2.14 on 2022-09-01 06:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0108_auto_20220901_1034'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='host',
|
||||
name='device_info',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='asset',
|
||||
name='info',
|
||||
field=models.JSONField(blank=True, default=dict, verbose_name='Info'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='database',
|
||||
name='version',
|
||||
field=models.CharField(blank=True, max_length=16, verbose_name='Version'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='DeviceInfo',
|
||||
),
|
||||
]
|
|
@ -16,7 +16,7 @@ __all__ = ['SystemUser']
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SystemUser(ProtocolMixin, BaseAccount):
|
||||
class SystemUser(BaseAccount, ProtocolMixin):
|
||||
LOGIN_AUTO = 'auto'
|
||||
LOGIN_MANUAL = 'manual'
|
||||
LOGIN_MODE_CHOICES = (
|
||||
|
@ -44,6 +44,7 @@ class SystemUser(ProtocolMixin, BaseAccount):
|
|||
# linux su 命令 (switch user)
|
||||
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
|
||||
su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from"))
|
||||
privileged = None
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
|
|
@ -2,14 +2,12 @@ from django.db import models
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
from common.db import fields
|
||||
from .base import BaseAccount, AbsConnectivity
|
||||
|
||||
__all__ = ['Account', 'AccountTemplate']
|
||||
|
||||
|
||||
class Account(BaseAccount, AbsConnectivity):
|
||||
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
|
||||
privileged = models.BooleanField(verbose_name=_("Privileged account"), default=False)
|
||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
|
||||
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
||||
|
@ -30,7 +28,6 @@ class Account(BaseAccount, AbsConnectivity):
|
|||
|
||||
|
||||
class AccountTemplate(BaseAccount, AbsConnectivity):
|
||||
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
|
||||
privileged = models.BooleanField(verbose_name=_("Privileged account"), default=False)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -11,7 +11,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from common.utils import lazyproperty
|
||||
from orgs.mixins.models import OrgManager, JMSOrgBaseModel
|
||||
from ...const import Category
|
||||
from ..platform import Platform
|
||||
from ..base import AbsConnectivity
|
||||
|
||||
|
@ -82,7 +81,7 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
|
|||
|
||||
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
|
||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
||||
|
||||
info = models.JSONField(verbose_name='Info', default=dict, blank=True)
|
||||
objects = AssetManager.from_queryset(AssetQuerySet)()
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -6,6 +6,7 @@ from .common import Asset
|
|||
|
||||
class Database(Asset):
|
||||
db_name = models.CharField(max_length=1024, verbose_name=_("Database"), blank=True)
|
||||
version = models.CharField(max_length=16, verbose_name=_("Version"), blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return '{}({}://{}/{})'.format(self.name, self.type, self.ip, self.db_name)
|
||||
|
|
|
@ -1,61 +1,8 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from common.mixins.models import CommonModelMixin
|
||||
from assets.const import Category
|
||||
from .common import Asset
|
||||
|
||||
|
||||
class Host(Asset):
|
||||
device_info = models.OneToOneField('DeviceInfo', null=True, on_delete=models.SET_NULL, verbose_name=_("Host"))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.category = Category.HOST
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class DeviceInfo(CommonModelMixin):
|
||||
# Collect
|
||||
vendor = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Vendor'))
|
||||
model = models.CharField(max_length=54, null=True, blank=True, verbose_name=_('Model'))
|
||||
sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number'))
|
||||
|
||||
cpu_model = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU model'))
|
||||
cpu_count = models.IntegerField(null=True, verbose_name=_('CPU count'))
|
||||
cpu_cores = models.IntegerField(null=True, verbose_name=_('CPU cores'))
|
||||
cpu_vcpus = models.IntegerField(null=True, verbose_name=_('CPU vcpus'))
|
||||
memory = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Memory'))
|
||||
disk_total = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk total'))
|
||||
disk_info = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk info'))
|
||||
|
||||
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
|
||||
os_version = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('OS version'))
|
||||
os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS arch'))
|
||||
hostname_raw = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hostname raw'))
|
||||
number = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Asset number'))
|
||||
|
||||
@property
|
||||
def cpu_info(self):
|
||||
info = ""
|
||||
if self.cpu_model:
|
||||
info += self.cpu_model
|
||||
if self.cpu_count and self.cpu_cores:
|
||||
info += "{}*{}".format(self.cpu_count, self.cpu_cores)
|
||||
return info
|
||||
|
||||
@property
|
||||
def hardware_info(self):
|
||||
if self.cpu_count:
|
||||
return '{} Core {} {}'.format(
|
||||
self.cpu_vcpus or self.cpu_count * self.cpu_cores,
|
||||
self.memory, self.disk_total
|
||||
)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def __str__(self):
|
||||
return '{} of {}'.format(self.hardware_info, self.host.name)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("DeviceInfo")
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class BaseAccount(OrgModelMixin, AuthMixin):
|
|||
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'))
|
||||
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
|
||||
# token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
|
||||
token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token'))
|
||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||
|
|
|
@ -22,8 +22,7 @@ class Domain(OrgModelMixin):
|
|||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, null=True,
|
||||
verbose_name=_('Date created'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Domain")
|
||||
|
@ -64,6 +63,7 @@ class Gateway(BaseAccount):
|
|||
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("Domain"))
|
||||
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
|
||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||
token = None
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from assets.const import Category, AllTypes
|
||||
from assets.const import AllTypes
|
||||
from common.db.fields import JsonDictTextField
|
||||
|
||||
|
||||
|
@ -30,23 +30,14 @@ class Platform(models.Model):
|
|||
meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta"))
|
||||
internal = models.BooleanField(default=False, verbose_name=_("Internal"))
|
||||
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
|
||||
domain_enabled = models.BooleanField(default=True, verbose_name=_("Domain enabled"))
|
||||
domain_default = models.ForeignKey(
|
||||
'assets.Domain', null=True, on_delete=models.SET_NULL,
|
||||
verbose_name=_("Domain default")
|
||||
)
|
||||
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
|
||||
protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
|
||||
# Accounts
|
||||
# 这应该和账号有关
|
||||
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
|
||||
su_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("SU method"))
|
||||
ping_enabled = models.BooleanField(default=False)
|
||||
ping_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
|
||||
verify_account_enabled = models.BooleanField(default=False, verbose_name=_("Verify account enabled"))
|
||||
verify_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Verify account method"))
|
||||
create_account_enabled = models.BooleanField(default=False, verbose_name=_("Create account enabled"))
|
||||
create_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Create account method"))
|
||||
change_password_enabled = models.BooleanField(default=False, verbose_name=_("Change password enabled"))
|
||||
change_password_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Change password method"))
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ from .common import BaseAccountSerializer
|
|||
|
||||
|
||||
class AccountSerializer(
|
||||
AccountTemplateSerializerMixin, AuthSerializerMixin,
|
||||
AccountTemplateSerializerMixin,
|
||||
AuthSerializerMixin,
|
||||
BulkOrgResourceModelSerializer
|
||||
):
|
||||
ip = serializers.ReadOnlyField(label=_("IP"))
|
||||
|
|
|
@ -1,226 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework import serializers
|
||||
from django.core.validators import RegexValidator
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from ..models import Asset, Node, Platform, SystemUser
|
||||
|
||||
__all__ = [
|
||||
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
|
||||
'ProtocolsField', 'PlatformSerializer',
|
||||
'AssetTaskSerializer', 'AssetsTaskSerializer', 'ProtocolsField',
|
||||
]
|
||||
|
||||
|
||||
class ProtocolField(serializers.RegexField):
|
||||
protocols = '|'.join(dict(Asset.Protocol.choices).keys())
|
||||
default_error_messages = {
|
||||
'invalid': _('Protocol format should {}/{}').format(protocols, '1-65535')
|
||||
}
|
||||
regex = r'^(%s)/(\d{1,5})$' % protocols
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(self.regex, **kwargs)
|
||||
|
||||
|
||||
def validate_duplicate_protocols(values):
|
||||
errors = []
|
||||
names = []
|
||||
|
||||
for value in values:
|
||||
if not value or '/' not in value:
|
||||
continue
|
||||
name = value.split('/')[0]
|
||||
if name in names:
|
||||
errors.append(_("Protocol duplicate: {}").format(name))
|
||||
names.append(name)
|
||||
errors.append('')
|
||||
if any(errors):
|
||||
raise serializers.ValidationError(errors)
|
||||
|
||||
|
||||
class ProtocolsField(serializers.ListField):
|
||||
default_validators = [validate_duplicate_protocols]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['child'] = ProtocolField()
|
||||
kwargs['allow_null'] = True
|
||||
kwargs['allow_empty'] = True
|
||||
kwargs['min_length'] = 1
|
||||
kwargs['max_length'] = 4
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
if not value:
|
||||
return []
|
||||
return value.split(' ')
|
||||
|
||||
|
||||
class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||
platform = serializers.SlugRelatedField(
|
||||
slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
|
||||
)
|
||||
protocols = ProtocolsField(label=_('Protocols'), required=False, default=['ssh/22'])
|
||||
domain_display = serializers.ReadOnlyField(source='domain.name', label=_('Domain name'))
|
||||
nodes_display = serializers.ListField(
|
||||
child=serializers.CharField(), label=_('Nodes name'), required=False
|
||||
)
|
||||
labels_display = serializers.ListField(
|
||||
child=serializers.CharField(), label=_('Labels name'), required=False, read_only=True
|
||||
)
|
||||
|
||||
"""
|
||||
资产的数据结构
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields_mini = ['id', 'hostname', 'ip', 'platform', 'protocols']
|
||||
fields_small = fields_mini + [
|
||||
'protocol', 'port', 'protocols', 'is_active',
|
||||
'public_ip', 'number', 'comment',
|
||||
]
|
||||
fields_hardware = [
|
||||
'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
|
||||
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
|
||||
'os', 'os_version', 'os_arch', 'hostname_raw',
|
||||
'cpu_info', 'hardware_info',
|
||||
]
|
||||
fields_fk = [
|
||||
'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display'
|
||||
]
|
||||
fields_m2m = [
|
||||
'nodes', 'nodes_display', 'labels', 'labels_display',
|
||||
]
|
||||
read_only_fields = [
|
||||
'connectivity', 'date_verified', 'cpu_info', 'hardware_info',
|
||||
'created_by', 'date_created',
|
||||
]
|
||||
fields = fields_small + fields_hardware + fields_fk + fields_m2m + read_only_fields
|
||||
extra_kwargs = {
|
||||
'protocol': {'write_only': True},
|
||||
'port': {'write_only': True},
|
||||
'hardware_info': {'label': _('Hardware info'), 'read_only': True},
|
||||
'admin_user_display': {'label': _('Admin user display'), 'read_only': True},
|
||||
'cpu_info': {'label': _('CPU info')},
|
||||
}
|
||||
|
||||
def get_fields(self):
|
||||
fields = super().get_fields()
|
||||
|
||||
admin_user_field = fields.get('admin_user')
|
||||
# 因为 mixin 中对 fields 有处理,可能不需要返回 admin_user
|
||||
if admin_user_field:
|
||||
admin_user_field.queryset = SystemUser.objects.filter(type=SystemUser.Type.admin)
|
||||
return fields
|
||||
|
||||
@classmethod
|
||||
def setup_eager_loading(cls, queryset):
|
||||
""" Perform necessary eager loading of data. """
|
||||
queryset = queryset.prefetch_related('domain', 'platform', 'admin_user')
|
||||
queryset = queryset.prefetch_related('nodes', 'labels')
|
||||
return queryset
|
||||
|
||||
def compatible_with_old_protocol(self, validated_data):
|
||||
protocols_data = validated_data.pop("protocols", [])
|
||||
|
||||
# 兼容老的api
|
||||
name = validated_data.get("protocol")
|
||||
port = validated_data.get("port")
|
||||
if not protocols_data and name and port:
|
||||
protocols_data.insert(0, '/'.join([name, str(port)]))
|
||||
elif not name and not port and protocols_data:
|
||||
protocol = protocols_data[0].split('/')
|
||||
validated_data["protocol"] = protocol[0]
|
||||
validated_data["port"] = int(protocol[1])
|
||||
if protocols_data:
|
||||
validated_data["protocols"] = ' '.join(protocols_data)
|
||||
|
||||
def perform_nodes_display_create(self, instance, nodes_display):
|
||||
if not nodes_display:
|
||||
return
|
||||
nodes_to_set = []
|
||||
for full_value in nodes_display:
|
||||
node = Node.objects.filter(full_value=full_value).first()
|
||||
if node:
|
||||
nodes_to_set.append(node)
|
||||
else:
|
||||
node = Node.create_node_by_full_value(full_value)
|
||||
nodes_to_set.append(node)
|
||||
instance.nodes.set(nodes_to_set)
|
||||
|
||||
def create(self, validated_data):
|
||||
self.compatible_with_old_protocol(validated_data)
|
||||
nodes_display = validated_data.pop('nodes_display', '')
|
||||
instance = super().create(validated_data)
|
||||
self.perform_nodes_display_create(instance, nodes_display)
|
||||
return instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
nodes_display = validated_data.pop('nodes_display', '')
|
||||
self.compatible_with_old_protocol(validated_data)
|
||||
instance = super().update(instance, validated_data)
|
||||
self.perform_nodes_display_create(instance, nodes_display)
|
||||
return instance
|
||||
|
||||
|
||||
class MiniAssetSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = AssetSerializer.Meta.fields_mini
|
||||
|
||||
|
||||
class PlatformSerializer(serializers.ModelSerializer):
|
||||
meta = serializers.DictField(required=False, allow_null=True, label=_('Meta'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# TODO 修复 drf SlugField RegexValidator bug,之后记得删除
|
||||
validators = self.fields['name'].validators
|
||||
if isinstance(validators[-1], RegexValidator):
|
||||
validators.pop()
|
||||
|
||||
class Meta:
|
||||
model = Platform
|
||||
fields = [
|
||||
'id', 'name', 'base', 'charset',
|
||||
'internal', 'meta', 'comment'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'internal': {'read_only': True},
|
||||
}
|
||||
|
||||
|
||||
class AssetSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = ['id', 'hostname', 'ip', 'port', 'connectivity', 'date_verified']
|
||||
|
||||
|
||||
class AssetsTaskSerializer(serializers.Serializer):
|
||||
ACTION_CHOICES = (
|
||||
('refresh', 'refresh'),
|
||||
('test', 'test'),
|
||||
)
|
||||
task = serializers.CharField(read_only=True)
|
||||
action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True)
|
||||
assets = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Asset.objects, required=False, allow_empty=True, many=True
|
||||
)
|
||||
|
||||
|
||||
class AssetTaskSerializer(AssetsTaskSerializer):
|
||||
ACTION_CHOICES = tuple(list(AssetsTaskSerializer.ACTION_CHOICES) + [
|
||||
('push_system_user', 'push_system_user'),
|
||||
('test_system_user', 'test_system_user')
|
||||
])
|
||||
action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True)
|
||||
asset = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Asset.objects, required=False, allow_empty=True, many=False
|
||||
)
|
||||
system_users = serializers.PrimaryKeyRelatedField(
|
||||
queryset=SystemUser.objects, required=False, allow_empty=True, many=True
|
||||
)
|
|
@ -1,2 +1,6 @@
|
|||
from .common import *
|
||||
from .category import *
|
||||
from .host import *
|
||||
from .database import *
|
||||
from .networking import *
|
||||
from .cloud import *
|
||||
from .web import *
|
||||
|
|
|
@ -1,49 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from assets.models import DeviceInfo, Host, Database, Networking, Cloud, Web
|
||||
from assets.models import Networking
|
||||
from .common import AssetSerializer
|
||||
|
||||
__all__ = [
|
||||
'DeviceSerializer', 'HostSerializer', 'DatabaseSerializer',
|
||||
'NetworkingSerializer', 'CloudSerializer', 'WebSerializer',
|
||||
]
|
||||
|
||||
|
||||
class DeviceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = DeviceInfo
|
||||
fields = [
|
||||
'id', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
|
||||
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
|
||||
'os', 'os_version', 'os_arch', 'hostname_raw', 'number',
|
||||
'cpu_info', 'hardware_info', 'date_updated'
|
||||
]
|
||||
|
||||
|
||||
class HostSerializer(AssetSerializer):
|
||||
device_info = DeviceSerializer(read_only=True, allow_null=True)
|
||||
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Host
|
||||
fields = AssetSerializer.Meta.fields + ['device_info']
|
||||
|
||||
|
||||
class DatabaseSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Database
|
||||
fields = AssetSerializer.Meta.fields + ['db_name']
|
||||
|
||||
|
||||
class WebSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Web
|
||||
fields = AssetSerializer.Meta.fields + ['url']
|
||||
|
||||
|
||||
class CloudSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Cloud
|
||||
fields = AssetSerializer.Meta.fields + ['cluster']
|
||||
__all__ = ['NetworkingSerializer']
|
||||
|
||||
|
||||
class NetworkingSerializer(AssetSerializer):
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
from assets.models import Cloud
|
||||
from .common import AssetSerializer
|
||||
|
||||
__all__ = ['CloudSerializer']
|
||||
|
||||
|
||||
class CloudSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Cloud
|
||||
fields = AssetSerializer.Meta.fields + ['cluster']
|
||||
|
|
@ -6,7 +6,7 @@ from django.db.transaction import atomic
|
|||
from django.db.models import F
|
||||
|
||||
from common.drf.serializers import JMSWritableNestedModelSerializer
|
||||
from common.drf.fields import ChoiceDisplayField
|
||||
from common.drf.fields import LabeledChoiceField, ObjectedRelatedField
|
||||
from ..account import AccountSerializer
|
||||
from ...models import Asset, Node, Platform, Protocol, Label, Domain
|
||||
from ...const import Category, AllTypes
|
||||
|
@ -42,33 +42,15 @@ class AssetPlatformSerializer(serializers.ModelSerializer):
|
|||
}
|
||||
|
||||
|
||||
class AssetDomainSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Domain
|
||||
fields = ['id', 'name']
|
||||
extra_kwargs = {
|
||||
'name': {'required': False}
|
||||
}
|
||||
|
||||
|
||||
class AssetNodesSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Node
|
||||
fields = ['id', 'value']
|
||||
extra_kwargs = {
|
||||
'value': {'required': False}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
platform = AssetPlatformSerializer(required=False)
|
||||
labels = AssetLabelSerializer(many=True, required=False)
|
||||
nodes = AssetNodesSerializer(many=True, required=False)
|
||||
accounts = AccountSerializer(many=True, required=False)
|
||||
protocols = AssetProtocolsSerializer(many=True, required=False)
|
||||
category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category'))
|
||||
type = LabeledChoiceField(choices=AllTypes.choices, read_only=True, label=_('Type'))
|
||||
domain = ObjectedRelatedField(required=False, queryset=Domain.objects, label=_('Domain'))
|
||||
platform = ObjectedRelatedField(required=False, queryset=Platform.objects, label=_('Platform'))
|
||||
nodes = ObjectedRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
|
||||
labels = AssetLabelSerializer(many=True, required=False, label=_('Labels'))
|
||||
accounts = AccountSerializer(many=True, required=False, label=_('Accounts'))
|
||||
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
|
||||
|
||||
"""
|
||||
资产的数据结构
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
from assets.models import Database
|
||||
from .common import AssetSerializer
|
||||
|
||||
__all__ = ['DatabaseSerializer']
|
||||
|
||||
|
||||
class DatabaseSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Database
|
||||
fields = AssetSerializer.Meta.fields + ['db_name']
|
|
@ -0,0 +1,37 @@
|
|||
from rest_framework import serializers
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from assets.models import Host
|
||||
from .common import AssetSerializer
|
||||
|
||||
|
||||
__all__ = ['HostInfoSerializer', 'HostSerializer']
|
||||
|
||||
|
||||
class HostInfoSerializer(serializers.Serializer):
|
||||
vendor = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Vendor'))
|
||||
model = serializers.CharField(max_length=54, required=False, allow_blank=True, label=_('Model'))
|
||||
sn = serializers.CharField(max_length=128, required=False, allow_blank=True, label=_('Serial number'))
|
||||
|
||||
cpu_model = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU model'))
|
||||
cpu_count = serializers.IntegerField(required=False, label=_('CPU count'))
|
||||
cpu_cores = serializers.IntegerField(required=False, label=_('CPU cores'))
|
||||
cpu_vcpus = serializers.IntegerField(required=False, label=_('CPU vcpus'))
|
||||
memory = serializers.CharField(max_length=64, allow_blank=True, required=False, label=_('Memory'))
|
||||
disk_total = serializers.CharField(max_length=1024, allow_blank=True, required=False, label=_('Disk total'))
|
||||
disk_info = serializers.CharField(max_length=1024, allow_blank=True, required=False, label=_('Disk info'))
|
||||
|
||||
os = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('OS'))
|
||||
os_version = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS version'))
|
||||
os_arch = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS arch'))
|
||||
hostname_raw = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Hostname raw'))
|
||||
number = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Asset number'))
|
||||
|
||||
|
||||
class HostSerializer(AssetSerializer):
|
||||
info = HostInfoSerializer(allow_null=True)
|
||||
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Host
|
||||
fields = AssetSerializer.Meta.fields + ['info']
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
from assets.models import Networking
|
||||
from .common import AssetSerializer
|
||||
|
||||
__all__ = ['NetworkingSerializer']
|
||||
|
||||
|
||||
class NetworkingSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Networking
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
from assets.models import Web
|
||||
from .common import AssetSerializer
|
||||
|
||||
__all__ = ['WebSerializer']
|
||||
|
||||
|
||||
class WebSerializer(AssetSerializer):
|
||||
class Meta(AssetSerializer.Meta):
|
||||
model = Web
|
||||
fields = AssetSerializer.Meta.fields + ['url']
|
|
@ -1,7 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from common.drf.fields import ChoiceDisplayField
|
||||
from common.drf.fields import LabeledChoiceField
|
||||
from common.drf.serializers import JMSWritableNestedModelSerializer
|
||||
from ..models import Platform, PlatformProtocol
|
||||
from ..const import Category, AllTypes
|
||||
|
@ -30,11 +30,11 @@ class PlatformProtocolsSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class PlatformSerializer(JMSWritableNestedModelSerializer):
|
||||
type = ChoiceDisplayField(choices=AllTypes.choices, label=_("Type"))
|
||||
category = ChoiceDisplayField(choices=Category.choices, label=_("Category"))
|
||||
type = LabeledChoiceField(choices=AllTypes.choices, label=_("Type"))
|
||||
category = LabeledChoiceField(choices=Category.choices, label=_("Category"))
|
||||
protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
|
||||
type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
|
||||
su_method = ChoiceDisplayField(
|
||||
su_method = LabeledChoiceField(
|
||||
choices=[('sudo', 'sudo su -'), ('su', 'su - ')],
|
||||
label='切换方式', required=False, default='sudo'
|
||||
)
|
||||
|
@ -54,6 +54,8 @@ class PlatformSerializer(JMSWritableNestedModelSerializer):
|
|||
]
|
||||
extra_kwargs = {
|
||||
'su_enabled': {'label': '启用切换账号'},
|
||||
'domain_enabled': {'label': "启用网域"},
|
||||
'domain_default': {'label': "默认网域"},
|
||||
'verify_account_enabled': {'label': '启用校验账号'},
|
||||
'verify_account_method': {'label': '校验账号方式'},
|
||||
'create_account_enabled': {'label': '启用创建账号'},
|
||||
|
|
|
@ -4,11 +4,13 @@ import six
|
|||
|
||||
from rest_framework.fields import ChoiceField
|
||||
from rest_framework import serializers
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from common.utils import decrypt_password
|
||||
|
||||
__all__ = [
|
||||
'ReadableHiddenField', 'EncryptedField', 'ChoiceDisplayField'
|
||||
'ReadableHiddenField', 'EncryptedField', 'LabeledChoiceField',
|
||||
'ObjectedRelatedField',
|
||||
]
|
||||
|
||||
|
||||
|
@ -40,9 +42,9 @@ class EncryptedField(serializers.CharField):
|
|||
return decrypt_password(value)
|
||||
|
||||
|
||||
class ChoiceDisplayField(ChoiceField):
|
||||
class LabeledChoiceField(ChoiceField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ChoiceDisplayField, self).__init__(*args, **kwargs)
|
||||
super(LabeledChoiceField, self).__init__(*args, **kwargs)
|
||||
self.choice_mapper = {
|
||||
six.text_type(key): value for key, value in self.choices.items()
|
||||
}
|
||||
|
@ -58,4 +60,31 @@ class ChoiceDisplayField(ChoiceField):
|
|||
def to_internal_value(self, data):
|
||||
if isinstance(data, dict):
|
||||
return data.get('value')
|
||||
return super(ChoiceDisplayField, self).to_internal_value(data)
|
||||
return super(LabeledChoiceField, self).to_internal_value(data)
|
||||
|
||||
|
||||
class ObjectedRelatedField(serializers.RelatedField):
|
||||
def __init__(self, **kwargs):
|
||||
self.attrs = kwargs.pop('attrs', None) or ('id', 'name')
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
data = {}
|
||||
for attr in self.attrs:
|
||||
data[attr] = getattr(value, attr)
|
||||
return data
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if isinstance(data, dict):
|
||||
pk = data.get(self.attrs[0])
|
||||
else:
|
||||
pk = data
|
||||
queryset = self.get_queryset()
|
||||
try:
|
||||
if isinstance(data, bool):
|
||||
raise TypeError
|
||||
return queryset.get(pk=pk)
|
||||
except ObjectDoesNotExist:
|
||||
self.fail('does_not_exist', pk_value=pk)
|
||||
except (TypeError, ValueError):
|
||||
self.fail('incorrect_type', data_type=type(pk).__name__)
|
||||
|
|
|
@ -85,13 +85,13 @@ class SimpleMetadataWithFilters(SimpleMetadata):
|
|||
field_info['choices'] = [
|
||||
{
|
||||
'value': choice_value,
|
||||
'display_name': force_text(choice_name, strings_only=True)
|
||||
'label': force_text(choice_name, strings_only=True)
|
||||
}
|
||||
for choice_value, choice_name in dict(field.choices).items()
|
||||
]
|
||||
|
||||
if field.__class__.__name__ == 'ChoiceDisplayField':
|
||||
field_info['type'] = 'display_choice'
|
||||
if field.__class__.__name__ == 'LabeledChoiceField':
|
||||
field_info['type'] = 'labeled_choice'
|
||||
return field_info
|
||||
|
||||
def get_filters_fields(self, request, view):
|
||||
|
|
Loading…
Reference in New Issue