diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py index a042323f5..2f3f11dae 100644 --- a/apps/accounts/api/account/task.py +++ b/apps/accounts/api/account/task.py @@ -31,8 +31,8 @@ class AccountsTaskCreateAPI(CreateAPIView): else: account = accounts[0] asset = account.asset - if not asset.auto_info['ansible_enabled'] or \ - not asset.auto_info['ping_enabled']: + if not asset.auto_config['ansible_enabled'] or \ + not asset.auto_config['ping_enabled']: raise NotSupportedTemporarilyError() task = verify_accounts_connectivity_task.delay(account_ids) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index d5b93dcd8..0a1cf3f83 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -158,7 +158,7 @@ class AccountAssetSerializer(serializers.ModelSerializer): class Meta: model = Asset - fields = ['id', 'name', 'address', 'type', 'category', 'platform', 'auto_info'] + fields = ['id', 'name', 'address', 'type', 'category', 'platform', 'auto_config'] def to_internal_value(self, data): if isinstance(data, dict): diff --git a/apps/assets/api/asset/__init__.py b/apps/assets/api/asset/__init__.py index c20e44573..0f1d81825 100644 --- a/apps/assets/api/asset/__init__.py +++ b/apps/assets/api/asset/__init__.py @@ -1,7 +1,8 @@ from .asset import * -from .host import * -from .database import * -from .web import * from .cloud import * +from .custom import * +from .database import * from .device import * +from .host import * from .permission import * +from .web import * diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 1e887e572..aa20bded9 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -102,14 +102,13 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ("platform", serializers.PlatformSerializer), ("suggestion", serializers.MiniAssetSerializer), ("gateways", serializers.GatewaySerializer), - ("spec_info", serializers.SpecSerializer), ) rbac_perms = ( ("match", "assets.match_asset"), ("platform", "assets.view_platform"), ("gateways", "assets.view_gateway"), ("spec_info", "assets.view_asset"), - ("info", "assets.view_asset"), + ("gathered_info", "assets.view_asset"), ) extra_filter_backends = [LabelFilterBackend, IpInFilterBackend, NodeFilterBackend] skip_assets = [] @@ -128,11 +127,6 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): serializer = super().get_serializer(instance=asset.platform) return Response(serializer.data) - @action(methods=["GET"], detail=True, url_path="spec-info") - def spec_info(self, *args, **kwargs): - asset = super().get_object() - return Response(asset.spec_info) - @action(methods=["GET"], detail=True, url_path="gateways") def gateways(self, *args, **kwargs): asset = self.get_object() @@ -163,6 +157,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): continue self.skip_assets.append(asset) return bulk_data + def bulk_update(self, request, *args, **kwargs): bulk_data = self.filter_bulk_update_data() request._full_data = bulk_data @@ -182,8 +177,8 @@ class AssetsTaskMixin: task = update_assets_hardware_info_manual(assets) else: asset = assets[0] - if not asset.auto_info['ansible_enabled'] or \ - not asset.auto_info['ping_enabled']: + if not asset.auto_config['ansible_enabled'] or \ + not asset.auto_config['ping_enabled']: raise NotSupportedTemporarilyError() task = test_assets_connectivity_manual(assets) return task diff --git a/apps/assets/api/asset/custom.py b/apps/assets/api/asset/custom.py new file mode 100644 index 000000000..ca5058ed6 --- /dev/null +++ b/apps/assets/api/asset/custom.py @@ -0,0 +1,16 @@ +from assets.models import Custom, Asset +from assets.serializers import CustomSerializer + +from .asset import AssetViewSet + +__all__ = ['CustomViewSet'] + + +class CustomViewSet(AssetViewSet): + model = Custom + perm_model = Asset + + def get_serializer_classes(self): + serializer_classes = super().get_serializer_classes() + serializer_classes['default'] = CustomSerializer + return serializer_classes diff --git a/apps/assets/api/asset/host.py b/apps/assets/api/asset/host.py index b92448bfb..d2ddc954d 100644 --- a/apps/assets/api/asset/host.py +++ b/apps/assets/api/asset/host.py @@ -1,8 +1,5 @@ -from rest_framework.decorators import action -from rest_framework.response import Response - from assets.models import Host, Asset -from assets.serializers import HostSerializer, HostInfoSerializer +from assets.serializers import HostSerializer from .asset import AssetViewSet __all__ = ['HostViewSet'] @@ -15,16 +12,4 @@ class HostViewSet(AssetViewSet): def get_serializer_classes(self): serializer_classes = super().get_serializer_classes() serializer_classes['default'] = HostSerializer - serializer_classes['info'] = HostInfoSerializer return serializer_classes - - @action(methods=["GET"], detail=True, url_path="info") - def info(self, *args, **kwargs): - asset = super().get_object() - serializer = self.get_serializer(asset.info) - data = serializer.data - data['asset'] = { - 'id': asset.id, 'name': asset.name, - 'address': asset.address - } - return Response(data) diff --git a/apps/assets/api/platform.py b/apps/assets/api/platform.py index be9456ff7..5d5d4523c 100644 --- a/apps/assets/api/platform.py +++ b/apps/assets/api/platform.py @@ -23,7 +23,7 @@ class AssetPlatformViewSet(JMSModelViewSet): def get_queryset(self): queryset = super().get_queryset() - queryset = queryset.filter(type__in=AllTypes.get_types()) + queryset = queryset.filter(type__in=AllTypes.get_types_values()) return queryset def get_object(self): diff --git a/apps/assets/automations/gather_facts/manager.py b/apps/assets/automations/gather_facts/manager.py index afd5ce523..e33d97617 100644 --- a/apps/assets/automations/gather_facts/manager.py +++ b/apps/assets/automations/gather_facts/manager.py @@ -29,7 +29,7 @@ class GatherFactsManager(BasePlaybookManager): asset = self.host_asset_mapper.get(host) if asset and info: info = self.format_asset_info(asset.type, info) - asset.info = info - asset.save(update_fields=['info']) + asset.gathered_info = info + asset.save(update_fields=['gathered_info']) else: logger.error("Not found info: {}".format(host)) diff --git a/apps/assets/automations/methods.py b/apps/assets/automations/methods.py index 86fcb775c..e1e12fb7f 100644 --- a/apps/assets/automations/methods.py +++ b/apps/assets/automations/methods.py @@ -1,8 +1,9 @@ -import os -import yaml import json +import os from functools import partial +import yaml + def check_platform_method(manifest, manifest_path): required_keys = ['category', 'method', 'name', 'id', 'type'] @@ -46,12 +47,12 @@ def filter_key(manifest, attr, value): return value in manifest_value or 'all' in manifest_value -def filter_platform_methods(category, tp, method=None, methods=None): +def filter_platform_methods(category, tp_name, method=None, methods=None): methods = platform_automation_methods if methods is None else methods if category: methods = filter(partial(filter_key, attr='category', value=category), methods) - if tp: - methods = filter(partial(filter_key, attr='type', value=tp), methods) + if tp_name: + methods = filter(partial(filter_key, attr='type', value=tp_name), methods) if method: methods = filter(lambda x: x['method'] == method, methods) return methods diff --git a/apps/assets/const/base.py b/apps/assets/const/base.py index 4b147f8ef..c90ab7320 100644 --- a/apps/assets/const/base.py +++ b/apps/assets/const/base.py @@ -4,6 +4,15 @@ from jumpserver.utils import has_valid_xpack_license from .protocol import Protocol +class Type: + def __init__(self, label, value): + self.label = label + self.value = value + + def __str__(self): + return self.value + + class BaseType(TextChoices): """ 约束应该考虑代是对平台对限制,避免多余对选项,如: mysql 开启 ssh, @@ -22,7 +31,7 @@ class BaseType(TextChoices): protocols_default = protocols.pop('*', {}) automation_default = automation.pop('*', {}) - for k, v in cls.choices: + for k, v in cls.get_choices(): tp_base = {**base_default, **base.get(k, {})} tp_auto = {**automation_default, **automation.get(k, {})} tp_protocols = {**protocols_default, **protocols.get(k, {})} @@ -37,8 +46,12 @@ class BaseType(TextChoices): choices = protocol.get('choices', []) if choices == '__self__': choices = [tp] - protocols = [{'name': name, **settings.get(name, {})} for name in choices] - protocols[0]['default'] = True + protocols = [ + {'name': name, **settings.get(name, {})} + for name in choices + ] + if protocols: + protocols[0]['default'] = True return protocols @classmethod @@ -58,21 +71,21 @@ class BaseType(TextChoices): raise NotImplementedError @classmethod - def get_community_types(cls): - raise NotImplementedError + def _get_choices_to_types(cls): + choices = cls.get_choices() + return [Type(label, value) for value, label in choices] @classmethod def get_types(cls): - tps = [tp for tp in cls] + tps = cls._get_choices_to_types() if not has_valid_xpack_license(): tps = cls.get_community_types() return tps + @classmethod + def get_community_types(cls): + return cls._get_choices_to_types() + @classmethod def get_choices(cls): - tps = cls.get_types() - cls_choices = cls.choices - return [ - choice for choice in cls_choices - if choice[0] in tps - ] + return cls.choices diff --git a/apps/assets/const/category.py b/apps/assets/const/category.py index e14867d69..8c4d387d8 100644 --- a/apps/assets/const/category.py +++ b/apps/assets/const/category.py @@ -3,7 +3,6 @@ from django.utils.translation import gettext_lazy as _ from common.db.models import ChoicesMixin - __all__ = ['Category'] @@ -13,13 +12,10 @@ class Category(ChoicesMixin, models.TextChoices): DATABASE = 'database', _("Database") CLOUD = 'cloud', _("Cloud service") WEB = 'web', _("Web") + CUSTOM = 'custom', _("Custom type") @classmethod def filter_choices(cls, category): _category = getattr(cls, category.upper(), None) choices = [(_category.value, _category.label)] if _category else cls.choices return choices - - - - diff --git a/apps/assets/const/custom.py b/apps/assets/const/custom.py new file mode 100644 index 000000000..be9cdcc92 --- /dev/null +++ b/apps/assets/const/custom.py @@ -0,0 +1,56 @@ +from .base import BaseType + + +class CustomTypes(BaseType): + @classmethod + def get_choices(cls): + types = cls.get_custom_platforms().values_list('type', flat=True).distinct() + return [(t, t) for t in types] + + @classmethod + def _get_base_constrains(cls) -> dict: + return { + '*': { + 'charset_enabled': False, + 'domain_enabled': False, + 'su_enabled': False, + }, + } + + @classmethod + def _get_automation_constrains(cls) -> dict: + constrains = { + '*': { + 'ansible_enabled': False, + 'ansible_config': {}, + 'gather_facts_enabled': False, + 'verify_account_enabled': False, + 'change_secret_enabled': False, + 'push_account_enabled': False, + 'gather_accounts_enabled': False, + } + } + return constrains + + @classmethod + def _get_protocol_constrains(cls) -> dict: + constrains = {} + for platform in cls.get_custom_platforms(): + choices = list(platform.protocols.values_list('name', flat=True)) + if platform.type in constrains: + choices = constrains[platform.type]['choices'] + choices + constrains[platform.type] = {'choices': choices} + return constrains + + @classmethod + def internal_platforms(cls): + return { + # cls.PUBLIC: [], + # cls.PRIVATE: [{'name': 'Vmware-vSphere'}], + # cls.K8S: [{'name': 'Kubernetes'}], + } + + @classmethod + def get_custom_platforms(cls): + from assets.models import Platform + return Platform.objects.filter(category='custom') diff --git a/apps/assets/const/protocol.py b/apps/assets/const/protocol.py index eef3269d3..a0b7b7ec7 100644 --- a/apps/assets/const/protocol.py +++ b/apps/assets/const/protocol.py @@ -141,6 +141,6 @@ class Protocol(ChoicesMixin, models.TextChoices): def protocol_secret_types(cls): settings = cls.settings() return { - protocol: settings[protocol]['secret_types'] + protocol: settings[protocol]['secret_types'] or ['password'] for protocol in cls.settings() } diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 3f2335ceb..a73b967b2 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -6,6 +6,7 @@ from django.utils.translation import gettext as _ from common.db.models import ChoicesMixin from .category import Category from .cloud import CloudTypes +from .custom import CustomTypes from .database import DatabaseTypes from .device import DeviceTypes from .host import HostTypes @@ -16,7 +17,7 @@ class AllTypes(ChoicesMixin): choices: list includes = [ HostTypes, DeviceTypes, DatabaseTypes, - CloudTypes, WebTypes, + CloudTypes, WebTypes, CustomTypes ] _category_constrains = {} @@ -24,22 +25,29 @@ class AllTypes(ChoicesMixin): def choices(cls): choices = [] for tp in cls.includes: - choices.extend(tp.choices) + choices.extend(tp.get_choices()) return choices + @classmethod + def get_choices(cls): + return cls.choices() + @classmethod def filter_choices(cls, category): - choices = dict(cls.category_types()).get(category, cls).choices + choices = dict(cls.category_types()).get(category, cls).get_choices() return choices() if callable(choices) else choices @classmethod - def get_constraints(cls, category, tp): + def get_constraints(cls, category, tp_name): + if not isinstance(tp_name, str): + tp_name = tp_name.value + types_cls = dict(cls.category_types()).get(category) if not types_cls: return {} type_constraints = types_cls.get_constrains() - constraints = type_constraints.get(tp, {}) - cls.set_automation_methods(category, tp, constraints) + constraints = type_constraints.get(tp_name, {}) + cls.set_automation_methods(category, tp_name, constraints) return constraints @classmethod @@ -56,7 +64,7 @@ class AllTypes(ChoicesMixin): return asset_methods + account_methods @classmethod - def set_automation_methods(cls, category, tp, constraints): + def set_automation_methods(cls, category, tp_name, constraints): from assets.automations import filter_platform_methods automation = constraints.get('automation', {}) automation_methods = {} @@ -66,7 +74,7 @@ class AllTypes(ChoicesMixin): continue item_name = item.replace('_enabled', '') methods = filter_platform_methods( - category, tp, item_name, methods=platform_automation_methods + category, tp_name, item_name, methods=platform_automation_methods ) methods = [{'name': m['name'], 'id': m['id']} for m in methods] automation_methods[item_name + '_methods'] = methods @@ -113,7 +121,7 @@ class AllTypes(ChoicesMixin): @classmethod def grouped_choices(cls): - grouped_types = [(str(ca), tp.choices) for ca, tp in cls.category_types()] + grouped_types = [(str(ca), tp.get_choices()) for ca, tp in cls.category_types()] return grouped_types @classmethod @@ -138,14 +146,20 @@ class AllTypes(ChoicesMixin): (Category.DATABASE, DatabaseTypes), (Category.CLOUD, CloudTypes), (Category.WEB, WebTypes), + (Category.CUSTOM, CustomTypes), ) @classmethod def get_types(cls): - tps = [] + choices = [] for i in dict(cls.category_types()).values(): - tps.extend(i.get_types()) - return tps + choices.extend(i.get_types()) + return choices + + @classmethod + def get_types_values(cls): + choices = cls.get_types() + return [c.value for c in choices] @staticmethod def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None): diff --git a/apps/assets/migrations/0111_auto_20230321_1633.py b/apps/assets/migrations/0111_auto_20230321_1633.py index fce00cc98..52dbc1971 100644 --- a/apps/assets/migrations/0111_auto_20230321_1633.py +++ b/apps/assets/migrations/0111_auto_20230321_1633.py @@ -28,7 +28,6 @@ def migrate_internal_platforms(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('assets', '0110_auto_20230315_1741'), ] @@ -39,6 +38,11 @@ class Migration(migrations.Migration): name='primary', field=models.BooleanField(default=False, verbose_name='Primary'), ), + migrations.AddField( + model_name='platformprotocol', + name='public', + field=models.BooleanField(default=True, verbose_name='Public'), + ), migrations.RunPython(migrate_platform_charset), migrations.RunPython(migrate_platform_protocol_primary), migrations.RunPython(migrate_internal_platforms), diff --git a/apps/assets/migrations/0112_auto_20230404_1631.py b/apps/assets/migrations/0112_auto_20230404_1631.py new file mode 100644 index 000000000..cf1c6268a --- /dev/null +++ b/apps/assets/migrations/0112_auto_20230404_1631.py @@ -0,0 +1,45 @@ +# Generated by Django 3.2.17 on 2023-04-04 08:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('assets', '0111_auto_20230321_1633'), + ] + + operations = [ + migrations.CreateModel( + name='Custom', + fields=[ + ('asset_ptr', + models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, + primary_key=True, serialize=False, to='assets.asset')), + ], + options={ + 'verbose_name': 'Custom asset', + }, + bases=('assets.asset',), + ), + migrations.AddField( + model_name='platform', + name='custom_fields', + field=models.JSONField(default=list, null=True, verbose_name='Custom fields'), + ), + migrations.AddField( + model_name='asset', + name='custom_info', + field=models.JSONField(default=dict, verbose_name='Custom info'), + ), + migrations.RenameField( + model_name='asset', + old_name='info', + new_name='gathered_info', + ), + migrations.AlterField( + model_name='asset', + name='gathered_info', + field=models.JSONField(blank=True, default=dict, verbose_name='Gathered info'), + ), + ] diff --git a/apps/assets/migrations/0112_platformprotocol_public.py b/apps/assets/migrations/0112_platformprotocol_public.py deleted file mode 100644 index 917686bc6..000000000 --- a/apps/assets/migrations/0112_platformprotocol_public.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.17 on 2023-03-24 03:11 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0111_auto_20230321_1633'), - ] - - operations = [ - migrations.AddField( - model_name='platformprotocol', - name='public', - field=models.BooleanField(default=True, verbose_name='Public'), - ), - ] diff --git a/apps/assets/models/asset/__init__.py b/apps/assets/models/asset/__init__.py index 793df7455..0004bfbb5 100644 --- a/apps/assets/models/asset/__init__.py +++ b/apps/assets/models/asset/__init__.py @@ -1,6 +1,7 @@ +from .cloud import * from .common import * -from .host import * +from .custom import * from .database import * from .device import * +from .host import * from .web import * -from .cloud import * diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 8643f4a6c..40874915f 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -108,7 +108,8 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): verbose_name=_("Nodes")) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels")) - info = models.JSONField(verbose_name=_('Info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息 + gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息 + custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict) objects = AssetManager.from_queryset(AssetQuerySet)() @@ -148,20 +149,26 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): return self.get_spec_values(instance, spec_fields) @lazyproperty - def auto_info(self): + def auto_config(self): platform = self.platform automation = self.platform.automation - return { + auto_config = { 'su_enabled': platform.su_enabled, - 'ping_enabled': automation.ping_enabled, 'domain_enabled': platform.domain_enabled, + 'ansible_enabled': False + } + if not automation: + return auto_config + auto_config.update({ + 'ping_enabled': automation.ping_enabled, 'ansible_enabled': automation.ansible_enabled, 'push_account_enabled': automation.push_account_enabled, 'gather_facts_enabled': automation.gather_facts_enabled, 'change_secret_enabled': automation.change_secret_enabled, 'verify_account_enabled': automation.verify_account_enabled, 'gather_accounts_enabled': automation.gather_accounts_enabled, - } + }) + return auto_config def get_target_ip(self): return self.address diff --git a/apps/assets/models/asset/custom.py b/apps/assets/models/asset/custom.py new file mode 100644 index 000000000..c3e4263ba --- /dev/null +++ b/apps/assets/models/asset/custom.py @@ -0,0 +1,8 @@ +from django.utils.translation import gettext_lazy as _ + +from .common import Asset + + +class Custom(Asset): + class Meta: + verbose_name = _("Custom asset") diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 7f57c01d7..08cd7e009 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -24,7 +24,7 @@ class PlatformProtocol(models.Model): @property def secret_types(self): - return Protocol.settings().get(self.name, {}).get('secret_types') + return Protocol.settings().get(self.name, {}).get('secret_types', ['password']) class PlatformAutomation(models.Model): @@ -69,14 +69,18 @@ class Platform(JMSBaseModel): internal = models.BooleanField(default=False, verbose_name=_("Internal")) # 资产有关的 charset = models.CharField( - default=CharsetChoices.utf8, choices=CharsetChoices.choices, max_length=8, verbose_name=_("Charset") + default=CharsetChoices.utf8, choices=CharsetChoices.choices, + max_length=8, verbose_name=_("Charset") ) domain_enabled = models.BooleanField(default=True, verbose_name=_("Domain enabled")) # 账号有关的 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")) - automation = models.OneToOneField(PlatformAutomation, on_delete=models.CASCADE, related_name='platform', - blank=True, null=True, verbose_name=_("Automation")) + automation = models.OneToOneField( + PlatformAutomation, on_delete=models.CASCADE, related_name='platform', + blank=True, null=True, verbose_name=_("Automation") + ) + custom_fields = models.JSONField(null=True, default=list, verbose_name=_("Custom fields")) @property def type_constraints(self): diff --git a/apps/assets/serializers/asset/__init__.py b/apps/assets/serializers/asset/__init__.py index 12f1eb66c..8e3e14cf3 100644 --- a/apps/assets/serializers/asset/__init__.py +++ b/apps/assets/serializers/asset/__init__.py @@ -1,6 +1,8 @@ +# No pass +from .cloud import * from .common import * -from .host import * +from .custom import * from .database import * from .device import * -from .cloud import * +from .host import * from .web import * diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 22329b22a..12c127dae 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # +import re + from django.db.models import F from django.db.transaction import atomic from django.utils.translation import ugettext_lazy as _ @@ -8,7 +10,9 @@ from rest_framework import serializers from accounts.models import Account from accounts.serializers import AccountSerializer -from common.serializers import WritableNestedModelSerializer, SecretReadableMixin, CommonModelSerializer +from common.serializers import WritableNestedModelSerializer, SecretReadableMixin, CommonModelSerializer, \ + MethodSerializer +from common.serializers.dynamic import create_serializer_class from common.serializers.fields import LabeledChoiceField from orgs.mixins.serializers import BulkOrgResourceModelSerializer from ...const import Category, AllTypes @@ -18,9 +22,11 @@ __all__ = [ 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer', 'AssetDetailSerializer', 'DetailMixin', 'AssetAccountSerializer', - 'AccountSecretSerializer', 'SpecSerializer' + 'AccountSecretSerializer', ] +uuid_pattern = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') + class AssetProtocolsSerializer(serializers.ModelSerializer): port = serializers.IntegerField(required=False, allow_null=True, max_value=65535, min_value=1) @@ -83,44 +89,32 @@ class AccountSecretSerializer(SecretReadableMixin, CommonModelSerializer): } -class SpecSerializer(serializers.Serializer): - # 数据库 - db_name = serializers.CharField(label=_("Database"), max_length=128, required=False) - use_ssl = serializers.BooleanField(label=_("Use SSL"), required=False) - allow_invalid_cert = serializers.BooleanField(label=_("Allow invalid cert"), required=False) - # Web - autofill = serializers.CharField(label=_("Auto fill"), required=False) - username_selector = serializers.CharField(label=_("Username selector"), required=False) - password_selector = serializers.CharField(label=_("Password selector"), required=False) - submit_selector = serializers.CharField(label=_("Submit selector"), required=False) - script = serializers.JSONField(label=_("Script"), required=False) - - class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSerializer): category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) labels = AssetLabelSerializer(many=True, required=False, label=_('Label')) protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'), default=()) - accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, label=_('Account')) + accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Account')) nodes_display = serializers.ListField(read_only=False, required=False, label=_("Node path")) + custom_info = MethodSerializer(label=_('Custom info')) class Meta: model = Asset fields_mini = ['id', 'name', 'address'] - fields_small = fields_mini + ['is_active', 'comment'] + fields_small = fields_mini + ['custom_info', 'is_active', 'comment'] fields_fk = ['domain', 'platform'] fields_m2m = [ 'nodes', 'labels', 'protocols', - 'nodes_display', 'accounts' + 'nodes_display', 'accounts', ] read_only_fields = [ - 'category', 'type', 'connectivity', 'auto_info', + 'category', 'type', 'connectivity', 'auto_config', 'date_verified', 'created_by', 'date_created', ] fields = fields_small + fields_fk + fields_m2m + read_only_fields - fields_unexport = ['auto_info'] + fields_unexport = ['auto_config'] extra_kwargs = { - 'auto_info': {'label': _('Auto info')}, + 'auto_config': {'label': _('Auto info')}, 'name': {'label': _("Name")}, 'address': {'label': _('Address')}, 'nodes_display': {'label': _('Node path')}, @@ -170,6 +164,36 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali .annotate(type=F("platform__type")) return queryset + def get_custom_info_serializer(self): + request = self.context.get('request') + default_field = serializers.DictField(required=False, label=_('Custom info')) + + if not request: + return default_field + + if self.instance and isinstance(self.instance, list): + return default_field + + if not self.instance and uuid_pattern.findall(request.path): + pk = uuid_pattern.findall(request.path)[0] + self.instance = Asset.objects.filter(id=pk).first() + + platform = None + if self.instance: + platform = self.instance.platform + elif request.query_params.get('platform'): + platform_id = request.query_params.get('platform') + platform_id = int(platform_id) if platform_id.isdigit() else 0 + platform = Platform.objects.filter(id=platform_id).first() + + if not platform: + return default_field + custom_fields = platform.custom_fields + if not custom_fields: + return default_field + name = platform.name.title() + 'CustomSerializer' + return create_serializer_class(name, custom_fields)() + @staticmethod def perform_nodes_display_create(instance, nodes_display): if not nodes_display: @@ -276,16 +300,46 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali class DetailMixin(serializers.Serializer): accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts')) - spec_info = serializers.DictField(label=_('Spec info'), read_only=True) - auto_info = serializers.DictField(read_only=True, label=_('Auto info')) + spec_info = MethodSerializer(label=_('Spec info'), read_only=True) + gathered_info = MethodSerializer(label=_('Gathered info'), read_only=True) + auto_config = serializers.DictField(read_only=True, label=_('Auto info')) + + def get_instance(self): + request = self.context.get('request') + if not self.instance and uuid_pattern.findall(request.path): + pk = uuid_pattern.findall(request.path)[0] + self.instance = Asset.objects.filter(id=pk).first() + return self.instance def get_field_names(self, declared_fields, info): names = super().get_field_names(declared_fields, info) names.extend([ - 'accounts', 'info', 'spec_info', 'auto_info' + 'accounts', 'gathered_info', 'spec_info', + 'auto_config', ]) return names + def get_category(self): + request = self.context.get('request') + if request.query_params.get('category'): + category = request.query_params.get('category') + else: + instance = self.get_instance() + category = instance.category + return category + + def get_gathered_info_serializer(self): + category = self.get_category() + from .info.gathered import category_gathered_serializer_map + serializer_cls = category_gathered_serializer_map.get(category, serializers.DictField) + return serializer_cls() + + def get_spec_info_serializer(self): + category = self.get_category() + from .info.spec import category_spec_serializer_map + serializer_cls = category_spec_serializer_map.get(category, serializers.DictField) + return serializer_cls() + class AssetDetailSerializer(DetailMixin, AssetSerializer): pass diff --git a/apps/assets/serializers/asset/custom.py b/apps/assets/serializers/asset/custom.py new file mode 100644 index 000000000..d88024218 --- /dev/null +++ b/apps/assets/serializers/asset/custom.py @@ -0,0 +1,9 @@ +from assets.models import Custom +from .common import AssetSerializer + +__all__ = ['CustomSerializer'] + + +class CustomSerializer(AssetSerializer): + class Meta(AssetSerializer.Meta): + model = Custom diff --git a/apps/assets/serializers/asset/database.py b/apps/assets/serializers/asset/database.py index 9f5e97dba..da6c9574a 100644 --- a/apps/assets/serializers/asset/database.py +++ b/apps/assets/serializers/asset/database.py @@ -1,9 +1,9 @@ -from rest_framework.serializers import ValidationError from django.utils.translation import ugettext_lazy as _ +from rest_framework.serializers import ValidationError from assets.models import Database +from assets.serializers.gateway import GatewayWithAccountSecretSerializer from .common import AssetSerializer -from ..gateway import GatewayWithAccountSecretSerializer __all__ = ['DatabaseSerializer', 'DatabaseWithGatewaySerializer'] diff --git a/apps/assets/serializers/asset/host.py b/apps/assets/serializers/asset/host.py index 0f6bd8f5d..c3575f43b 100644 --- a/apps/assets/serializers/asset/host.py +++ b/apps/assets/serializers/asset/host.py @@ -1,34 +1,18 @@ from django.utils.translation import gettext_lazy as _ -from rest_framework import serializers from assets.models import Host from .common import AssetSerializer +from .info.gathered import HostGatheredInfoSerializer -__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, allow_blank=True, required=False, label=_('CPU model')) - cpu_count = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU count')) - cpu_cores = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU cores')) - cpu_vcpus = serializers.CharField(max_length=64, required=False, allow_blank=True, 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')) - - distribution = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('OS')) - distribution_version = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS version')) - arch = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS arch')) +__all__ = ['HostSerializer'] class HostSerializer(AssetSerializer): - info = HostInfoSerializer(required=False, label=_('Info')) + gathered_info = HostGatheredInfoSerializer(required=False, read_only=True, label=_("Gathered info")) class Meta(AssetSerializer.Meta): model = Host - fields = AssetSerializer.Meta.fields + ['info'] + fields = AssetSerializer.Meta.fields + ['gathered_info'] extra_kwargs = { **AssetSerializer.Meta.extra_kwargs, 'address': { diff --git a/apps/assets/serializers/asset/info/gathered.py b/apps/assets/serializers/asset/info/gathered.py new file mode 100644 index 000000000..db0f22e65 --- /dev/null +++ b/apps/assets/serializers/asset/info/gathered.py @@ -0,0 +1,23 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + + +class HostGatheredInfoSerializer(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, allow_blank=True, required=False, label=_('CPU model')) + cpu_count = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU count')) + cpu_cores = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('CPU cores')) + cpu_vcpus = serializers.CharField(max_length=64, required=False, allow_blank=True, 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')) + + distribution = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('OS')) + distribution_version = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS version')) + arch = serializers.CharField(max_length=16, allow_blank=True, required=False, label=_('OS arch')) + + +category_gathered_serializer_map = { + 'host': HostGatheredInfoSerializer, +} diff --git a/apps/assets/serializers/asset/info/spec.py b/apps/assets/serializers/asset/info/spec.py new file mode 100644 index 000000000..5d3de54c3 --- /dev/null +++ b/apps/assets/serializers/asset/info/spec.py @@ -0,0 +1,24 @@ +from rest_framework import serializers + +from assets.models import Database, Web + + +class DatabaseSpecSerializer(serializers.ModelSerializer): + class Meta: + model = Database + fields = ['db_name', 'use_ssl', 'allow_invalid_cert'] + + +class WebSpecSerializer(serializers.ModelSerializer): + class Meta: + model = Web + fields = [ + 'autofill', 'username_selector', 'password_selector', + 'submit_selector', 'script' + ] + + +category_spec_serializer_map = { + 'database': DatabaseSpecSerializer, + 'web': WebSpecSerializer, +} diff --git a/apps/assets/serializers/gateway.py b/apps/assets/serializers/gateway.py index 78d8afda5..259bc13f8 100644 --- a/apps/assets/serializers/gateway.py +++ b/apps/assets/serializers/gateway.py @@ -3,8 +3,8 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers -from .asset import HostSerializer from .asset.common import AccountSecretSerializer +from .asset.host import HostSerializer from ..models import Gateway, Asset __all__ = ['GatewaySerializer', 'GatewayWithAccountSecretSerializer'] diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 300b3dd65..ba9def8b9 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -4,6 +4,7 @@ from rest_framework import serializers from assets.const.web import FillType from common.serializers import WritableNestedModelSerializer from common.serializers.fields import LabeledChoiceField +from common.utils import lazyproperty from ..const import Category, AllTypes from ..models import Platform, PlatformProtocol, PlatformAutomation @@ -37,7 +38,6 @@ class ProtocolSettingSerializer(serializers.Serializer): default="", allow_blank=True, label=_("Submit selector") ) script = serializers.JSONField(default=list, label=_("Script")) - # Redis auth_username = serializers.BooleanField(default=False, label=_("Auth with username")) @@ -87,6 +87,21 @@ class PlatformProtocolSerializer(serializers.ModelSerializer): ] +class PlatformCustomField(serializers.Serializer): + TYPE_CHOICES = [ + ("str", "str"), + ("int", "int"), + ("bool", "bool"), + ("choice", "choice"), + ] + name = serializers.CharField(label=_("Name"), max_length=128) + label = serializers.CharField(label=_("Label"), max_length=128) + type = serializers.ChoiceField(choices=TYPE_CHOICES, label=_("Type"), default='str') + default = serializers.CharField(default="", allow_blank=True, label=_("Default"), max_length=1024) + help_text = serializers.CharField(default="", allow_blank=True, label=_("Help text"), max_length=1024) + choices = serializers.ListField(default=list, label=_("Choices"), required=False) + + class PlatformSerializer(WritableNestedModelSerializer): SU_METHOD_CHOICES = [ ("sudo", "sudo su -"), @@ -95,19 +110,16 @@ class PlatformSerializer(WritableNestedModelSerializer): ("super", "super 15"), ("super_level", "super level 15") ] - - charset = LabeledChoiceField( - choices=Platform.CharsetChoices.choices, label=_("Charset") - ) + charset = LabeledChoiceField(choices=Platform.CharsetChoices.choices, label=_("Charset"), default='utf-8') type = LabeledChoiceField(choices=AllTypes.choices(), label=_("Type")) category = LabeledChoiceField(choices=Category.choices, label=_("Category")) - protocols = PlatformProtocolSerializer( - label=_("Protocols"), many=True, required=False + protocols = PlatformProtocolSerializer(label=_("Protocols"), many=True, required=False) + automation = PlatformAutomationSerializer(label=_("Automation"), required=False, default=dict) + su_method = LabeledChoiceField( + choices=SU_METHOD_CHOICES, label=_("Su method"), + required=False, default="sudo", allow_null=True ) - automation = PlatformAutomationSerializer(label=_("Automation"), required=False) - su_method = LabeledChoiceField(choices=SU_METHOD_CHOICES, - label=_("Su method"), required=False, default="sudo", allow_null=True - ) + custom_fields = PlatformCustomField(label=_("Custom fields"), many=True, required=False) class Meta: model = Platform @@ -115,19 +127,54 @@ class PlatformSerializer(WritableNestedModelSerializer): fields_small = fields_mini + [ "category", "type", "charset", ] - fields_other = [ - 'date_created', 'date_updated', 'created_by', 'updated_by', + read_only_fields = [ + 'internal', 'date_created', 'date_updated', + 'created_by', 'updated_by' ] fields = fields_small + [ "protocols", "domain_enabled", "su_enabled", - "su_method", "automation", "comment", - ] + fields_other + "su_method", "automation", "comment", "custom_fields", + ] + read_only_fields extra_kwargs = { "su_enabled": {"label": _('Su enabled')}, "domain_enabled": {"label": _('Domain enabled')}, "domain_default": {"label": _('Default Domain')}, } + @property + def platform_category_type(self): + if self.instance: + return self.instance.category, self.instance.type + if self.initial_data: + return self.initial_data.get('category'), self.initial_data.get('type') + raise serializers.ValidationError({'type': _("type is required")}) + + def add_type_choices(self, name, label): + tp = self.fields['type'] + tp.choices[name] = label + tp.choice_mapper[name] = label + tp.choice_strings_to_values[name] = label + + @lazyproperty + def constraints(self): + category, tp = self.platform_category_type + constraints = AllTypes.get_constraints(category, tp) + return constraints + + def validate(self, attrs): + domain_enabled = attrs.get('domain_enabled', False) and self.constraints.get('domain_enabled', False) + su_enabled = attrs.get('su_enabled', False) and self.constraints.get('su_enabled', False) + automation = attrs.get('automation', {}) + automation['ansible_enabled'] = automation.get('ansible_enabled', False) \ + and self.constraints.get('ansible_enabled', False) + attrs.update({ + 'domain_enabled': domain_enabled, + 'su_enabled': su_enabled, + 'automation': automation, + }) + self.initial_data['automation'] = automation + return attrs + @classmethod def setup_eager_loading(cls, queryset): queryset = queryset.prefetch_related( diff --git a/apps/assets/signal_handlers/asset.py b/apps/assets/signal_handlers/asset.py index 107be4422..9598bec5d 100644 --- a/apps/assets/signal_handlers/asset.py +++ b/apps/assets/signal_handlers/asset.py @@ -66,11 +66,11 @@ def on_asset_create(sender, instance=None, created=False, **kwargs): ensure_asset_has_node(assets=(instance,)) # 获取资产硬件信息 - auto_info = instance.auto_info - if auto_info.get('ping_enabled'): + auto_config = instance.auto_config + if auto_config.get('ping_enabled'): logger.debug('Asset {} ping enabled, test connectivity'.format(instance.name)) test_assets_connectivity_handler(assets=(instance,)) - if auto_info.get('gather_facts_enabled'): + if auto_config.get('gather_facts_enabled'): logger.debug('Asset {} gather facts enabled, gather facts'.format(instance.name)) gather_assets_facts_handler(assets=(instance,)) diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index a9a385eb9..174dccef1 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -14,6 +14,7 @@ router.register(r'devices', api.DeviceViewSet, 'device') router.register(r'databases', api.DatabaseViewSet, 'database') router.register(r'webs', api.WebViewSet, 'web') router.register(r'clouds', api.CloudViewSet, 'cloud') +router.register(r'customs', api.CustomViewSet, 'custom') router.register(r'platforms', api.AssetPlatformViewSet, 'platform') router.register(r'labels', api.LabelViewSet, 'label') router.register(r'nodes', api.NodeViewSet, 'node') diff --git a/apps/authentication/serializers/connect_token_secret.py b/apps/authentication/serializers/connect_token_secret.py index 003d8239e..f5f5bc62a 100644 --- a/apps/authentication/serializers/connect_token_secret.py +++ b/apps/authentication/serializers/connect_token_secret.py @@ -5,7 +5,8 @@ from accounts.const import SecretType from accounts.models import Account from acls.models import CommandGroup, CommandFilterACL from assets.models import Asset, Platform, Gateway, Domain -from assets.serializers import PlatformSerializer, AssetProtocolsSerializer +from assets.serializers.asset import AssetProtocolsSerializer +from assets.serializers.platform import PlatformSerializer from common.serializers.fields import LabeledChoiceField from common.serializers.fields import ObjectRelatedField from orgs.mixins.serializers import OrgResourceModelSerializerMixin @@ -30,14 +31,12 @@ class _ConnectionTokenAssetSerializer(serializers.ModelSerializer): class Meta: model = Asset fields = [ - 'id', 'name', 'address', 'protocols', - 'category', 'type', 'org_id', 'spec_info', - 'secret_info', + 'id', 'name', 'address', 'protocols', 'category', + 'type', 'org_id', 'spec_info', 'secret_info', ] class _SimpleAccountSerializer(serializers.ModelSerializer): - """ Account """ secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type')) class Meta: @@ -46,20 +45,18 @@ class _SimpleAccountSerializer(serializers.ModelSerializer): class _ConnectionTokenAccountSerializer(serializers.ModelSerializer): - """ Account """ su_from = _SimpleAccountSerializer(required=False, label=_('Su from')) secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type')) class Meta: model = Account fields = [ - 'id', 'name', 'username', 'secret_type', 'secret', 'su_from', 'privileged' + 'id', 'name', 'username', 'secret_type', + 'secret', 'su_from', 'privileged' ] class _ConnectionTokenGatewaySerializer(serializers.ModelSerializer): - """ Gateway """ - account = _SimpleAccountSerializer( required=False, source='select_account', read_only=True ) @@ -85,7 +82,8 @@ class _ConnectionTokenCommandFilterACLSerializer(serializers.ModelSerializer): class Meta: model = CommandFilterACL fields = [ - 'id', 'name', 'command_groups', 'action', 'reviewers', 'priority', 'is_active' + 'id', 'name', 'command_groups', 'action', + 'reviewers', 'priority', 'is_active' ] @@ -136,8 +134,7 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin): 'id', 'value', 'user', 'asset', 'account', 'platform', 'command_filter_acls', 'protocol', 'domain', 'gateway', 'actions', 'expire_at', - 'from_ticket', - 'expire_now', 'connect_method', + 'from_ticket', 'expire_now', 'connect_method', ] extra_kwargs = { 'value': {'read_only': True}, diff --git a/apps/common/serializers/dynamic.py b/apps/common/serializers/dynamic.py new file mode 100644 index 000000000..22496dfc2 --- /dev/null +++ b/apps/common/serializers/dynamic.py @@ -0,0 +1,54 @@ +from rest_framework import serializers + +example_info = [ + {"name": "name", "label": "姓名", "required": False, "default": "老广", "type": "str"}, + {"name": "age", "label": "年龄", "required": False, "default": 18, "type": "int"}, +] + +type_field_map = { + "str": serializers.CharField, + "int": serializers.IntegerField, + "bool": serializers.BooleanField, + "text": serializers.CharField, + "choice": serializers.ChoiceField, +} + + +def set_default_if_need(data, i): + field_name = data.pop('name', 'Attr{}'.format(i + 1)) + data['name'] = field_name + + if not data.get('label'): + data['label'] = field_name + return data + + +def set_default_by_type(tp, data, field_info): + if tp == 'str': + data['max_length'] = 4096 + elif tp == 'choice': + choices = field_info.pop('choices', []) + if isinstance(choices, str): + choices = choices.split(',') + choices = [ + (c, c.title()) if not isinstance(c, (tuple, list)) else c + for c in choices + ] + data['choices'] = choices + return data + + +def create_serializer_class(serializer_name, fields_info): + serializer_fields = {} + fields_name = ['name', 'label', 'default', 'type', 'help_text'] + + for i, field_info in enumerate(fields_info): + data = {k: field_info.get(k) for k in fields_name} + field_type = data.pop('type', 'str') + data = set_default_by_type(field_type, data, field_info) + data = set_default_if_need(data, i) + field_name = data.pop('name') + field_class = type_field_map.get(field_type, serializers.CharField) + serializer_fields[field_name] = field_class(**data) + + return type(serializer_name, (serializers.Serializer,), serializer_fields) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 74bd80fbd..86fc7369d 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15e96f9f31e92077ac828e248a30678e53b7c867757ae6348ae9805bc64874bc -size 138124 +oid sha256:975e9e264596ef5f7233fc1d2fb45281a5fe13f5a722fc2b9d5c40562ada069d +size 138303 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index e5af1f351..772fb0b5c 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-03 18:06+0800\n" +"POT-Creation-Date: 2023-04-07 13:57+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -176,12 +176,13 @@ msgstr "作成のみ" #: accounts/models/account.py:47 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:110 +#: accounts/serializers/account/account.py:173 +#: accounts/serializers/account/account.py:206 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/models/base.py:100 acls/serializers/base.py:56 -#: assets/models/asset/common.py:92 assets/models/asset/common.py:261 +#: assets/models/asset/common.py:92 assets/models/asset/common.py:268 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 #: assets/serializers/label.py:27 audits/models.py:48 #: authentication/models/connection_token.py:33 @@ -192,8 +193,8 @@ msgstr "作成のみ" msgid "Asset" msgstr "資産" -#: accounts/models/account.py:51 accounts/serializers/account/account.py:114 -#: authentication/serializers/connect_token_secret.py:50 +#: accounts/models/account.py:51 accounts/serializers/account/account.py:178 +#: authentication/serializers/connect_token_secret.py:48 msgid "Su from" msgstr "から切り替え" @@ -202,16 +203,22 @@ msgstr "から切り替え" msgid "Version" msgstr "バージョン" -#: accounts/models/account.py:55 accounts/serializers/account/account.py:111 +#: accounts/models/account.py:55 accounts/serializers/account/account.py:174 #: users/models/user.py:768 msgid "Source" msgstr "ソース" -#: accounts/models/account.py:58 +#: accounts/models/account.py:56 +#, fuzzy +#| msgid "Source" +msgid "Source ID" +msgstr "ソース" + +#: accounts/models/account.py:59 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 #: acls/models/base.py:102 acls/serializers/base.py:57 -#: assets/serializers/asset/common.py:135 assets/serializers/gateway.py:28 +#: assets/serializers/asset/common.py:107 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:21 terminal/models/session/session.py:34 @@ -219,35 +226,35 @@ msgstr "ソース" msgid "Account" msgstr "アカウント" -#: accounts/models/account.py:64 +#: accounts/models/account.py:65 msgid "Can view asset account secret" msgstr "資産アカウントの秘密を表示できます" -#: accounts/models/account.py:65 +#: accounts/models/account.py:66 msgid "Can view asset history account" msgstr "資産履歴アカウントを表示できます" -#: accounts/models/account.py:66 +#: accounts/models/account.py:67 msgid "Can view asset history account secret" msgstr "資産履歴アカウントパスワードを表示できます" -#: accounts/models/account.py:67 +#: accounts/models/account.py:68 msgid "Can verify account" msgstr "アカウントを確認できます" -#: accounts/models/account.py:68 +#: accounts/models/account.py:69 msgid "Can push account" msgstr "アカウントをプッシュできます" -#: accounts/models/account.py:109 +#: accounts/models/account.py:110 msgid "Account template" msgstr "アカウント テンプレート" -#: accounts/models/account.py:114 +#: accounts/models/account.py:115 msgid "Can view asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを表示できます" -#: accounts/models/account.py:115 +#: accounts/models/account.py:116 msgid "Can change asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを変更できます" @@ -344,12 +351,11 @@ msgid "Can add push account execution" msgstr "プッシュ アカウントの作成の実行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:175 +#: accounts/serializers/account/account.py:383 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 -#: assets/serializers/asset/common.py:77 -#: authentication/serializers/connect_token_secret.py:41 -#: authentication/serializers/connect_token_secret.py:51 +#: authentication/serializers/connect_token_secret.py:40 +#: authentication/serializers/connect_token_secret.py:49 msgid "Secret type" msgstr "鍵の種類" @@ -394,7 +400,8 @@ msgstr "開始日" msgid "Date finished" msgstr "終了日" -#: accounts/models/automations/change_secret.py:94 assets/const/automation.py:8 +#: accounts/models/automations/change_secret.py:94 +#: accounts/serializers/account/account.py:208 assets/const/automation.py:8 #: common/const/choices.py:20 msgid "Error" msgstr "間違い" @@ -450,7 +457,7 @@ msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 #: acls/serializers/base.py:81 acls/serializers/login_acl.py:25 #: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 -#: authentication/serializers/connect_token_secret.py:109 +#: authentication/serializers/connect_token_secret.py:107 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" msgstr "アクション" @@ -469,10 +476,10 @@ msgstr "アカウントの確認" #: assets/models/asset/common.py:90 assets/models/asset/common.py:102 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 -#: assets/models/platform.py:13 assets/models/platform.py:64 -#: assets/serializers/asset/common.py:74 assets/serializers/asset/common.py:155 -#: assets/serializers/platform.py:148 -#: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21 +#: assets/models/platform.py:13 assets/models/platform.py:65 +#: assets/serializers/asset/common.py:128 assets/serializers/platform.py:93 +#: assets/serializers/platform.py:184 +#: authentication/serializers/connect_token_secret.py:101 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:91 ops/models/playbook.py:23 ops/serializers/job.py:19 #: orgs/models.py:69 perms/models/asset_permission.py:56 rbac/models/role.py:29 @@ -480,7 +487,7 @@ msgstr "アカウントの確認" #: terminal/models/applet/applet.py:27 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:90 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:15 -#: terminal/models/component/terminal.py:84 users/forms/profile.py:33 +#: terminal/models/component/terminal.py:85 users/forms/profile.py:33 #: users/models/group.py:13 users/models/user.py:717 #: xpack/plugins/cloud/models.py:28 msgid "Name" @@ -493,7 +500,7 @@ msgstr "特権アカウント" #: accounts/models/base.py:40 assets/models/asset/common.py:109 #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 -#: authentication/serializers/connect_token_secret.py:107 +#: authentication/serializers/connect_token_secret.py:105 #: terminal/models/applet/applet.py:32 users/serializers/user.py:162 msgid "Is active" msgstr "アクティブです。" @@ -539,38 +546,38 @@ msgstr "" "{} -暗号化変更タスクが完了しました: 暗号化パスワードが設定されていません-個人" "情報にアクセスしてください-> ファイル暗号化パスワードを設定してください" -#: accounts/serializers/account/account.py:80 -#: assets/serializers/asset/common.py:72 settings/serializers/auth/sms.py:75 +#: accounts/serializers/account/account.py:26 +#: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "テンプレート" -#: accounts/serializers/account/account.py:83 -#: assets/serializers/asset/common.py:69 +#: accounts/serializers/account/account.py:29 msgid "Push now" msgstr "今すぐプッシュ" -#: accounts/serializers/account/account.py:85 -#: accounts/serializers/account/base.py:64 -msgid "Has secret" -msgstr "エスクローされたパスワード" +#: accounts/serializers/account/account.py:33 +#, fuzzy +#| msgid "Runas policy" +msgid "Exist policy" +msgstr "ユーザー ポリシー" -#: accounts/serializers/account/account.py:90 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:65 -#: assets/serializers/asset/common.py:131 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:100 assets/serializers/platform.py:149 +#: accounts/serializers/account/account.py:153 applications/models.py:11 +#: assets/models/label.py:21 assets/models/platform.py:66 +#: assets/serializers/asset/common.py:103 assets/serializers/cagegory.py:8 +#: assets/serializers/platform.py:111 assets/serializers/platform.py:185 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" msgstr "カテゴリ" -#: accounts/serializers/account/account.py:91 +#: accounts/serializers/account/account.py:154 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:18 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:66 -#: assets/serializers/asset/common.py:132 assets/serializers/platform.py:99 -#: audits/serializers.py:48 -#: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:102 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:67 +#: assets/serializers/asset/common.py:104 assets/serializers/platform.py:95 +#: assets/serializers/platform.py:110 audits/serializers.py:48 +#: authentication/serializers/connect_token_secret.py:114 ops/models/job.py:102 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -582,15 +589,56 @@ msgstr "カテゴリ" msgid "Type" msgstr "タイプ" -#: accounts/serializers/account/account.py:106 +#: accounts/serializers/account/account.py:169 msgid "Asset not found" msgstr "資産が存在しません" -#: accounts/serializers/account/account.py:118 ops/models/base.py:19 -msgid "Account policy" -msgstr "アカウント ポリシー" +#: accounts/serializers/account/account.py:175 +#: accounts/serializers/account/base.py:64 +msgid "Has secret" +msgstr "エスクローされたパスワード" -#: accounts/serializers/account/account.py:185 acls/models/base.py:98 +#: accounts/serializers/account/account.py:207 ops/models/celery.py:60 +#: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 +#: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 +#: tickets/serializers/ticket/ticket.py:21 +msgid "State" +msgstr "状態" + +#: accounts/serializers/account/account.py:209 +#, fuzzy +#| msgid "Change by" +msgid "Changed" +msgstr "による変更" + +#: accounts/serializers/account/account.py:213 +#: accounts/serializers/automations/base.py:22 +#: assets/models/automations/base.py:19 +#: assets/serializers/automations/base.py:20 ops/models/base.py:17 +#: ops/models/job.py:104 ops/serializers/job.py:20 +#: terminal/templates/terminal/_msg_command_execute_alert.html:16 +msgid "Assets" +msgstr "資産" + +#: accounts/serializers/account/account.py:284 +#, fuzzy +#| msgid "Name already exists" +msgid "Account already exists" +msgstr "名前は既に存在します。" + +#: accounts/serializers/account/account.py:320 +#, fuzzy, python-format +#| msgid "Current user not support mfa type: {}" +msgid "Asset does not support this secret type: %s" +msgstr "現在のユーザーはmfaタイプをサポートしていません: {}" + +#: accounts/serializers/account/account.py:351 +#, fuzzy +#| msgid "Account name" +msgid "Account has exist" +msgstr "アカウント名" + +#: accounts/serializers/account/account.py:393 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:55 #: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 @@ -608,7 +656,7 @@ msgstr "アカウント ポリシー" msgid "User" msgstr "ユーザー" -#: accounts/serializers/account/account.py:186 +#: accounts/serializers/account/account.py:394 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -639,19 +687,11 @@ msgstr "資産タイプ" msgid "Key password" msgstr "キーパスワード" -#: accounts/serializers/account/base.py:81 -#: assets/serializers/asset/common.py:307 +#: accounts/serializers/account/base.py:80 +#: assets/serializers/asset/common.py:298 msgid "Spec info" msgstr "特別情報" -#: accounts/serializers/automations/base.py:22 -#: assets/models/automations/base.py:19 -#: assets/serializers/automations/base.py:20 ops/models/base.py:17 -#: ops/models/job.py:104 ops/serializers/job.py:20 -#: terminal/templates/terminal/_msg_command_execute_alert.html:16 -msgid "Assets" -msgstr "資産" - #: accounts/serializers/automations/base.py:23 #: assets/models/asset/common.py:108 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 @@ -734,11 +774,6 @@ msgstr "パスワードには `\"` を含まない" msgid "private key invalid or passphrase error" msgstr "秘密鍵が無効またはpassphraseエラー" -#: accounts/validator.py:41 -#, python-brace-format -msgid "{field_name} not a legal option" -msgstr "" - #: acls/apps.py:7 msgid "Acls" msgstr "Acls" @@ -768,7 +803,7 @@ msgstr "1-100、低い値は最初に一致します" #: acls/models/base.py:82 acls/serializers/base.py:75 #: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 -#: authentication/serializers/connect_token_secret.py:82 +#: authentication/serializers/connect_token_secret.py:79 msgid "Reviewers" msgstr "レビュー担当者" @@ -793,7 +828,7 @@ msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/basic.py:10 xpack/plugins/license/models.py:30 +#: settings/serializers/basic.py:10 xpack/plugins/license/models.py:29 msgid "Content" msgstr "コンテンツ" @@ -807,7 +842,7 @@ msgstr "家を無視する" #: acls/models/command_acl.py:33 acls/models/command_acl.py:96 #: acls/serializers/command_acl.py:28 -#: authentication/serializers/connect_token_secret.py:79 +#: authentication/serializers/connect_token_secret.py:76 msgid "Command group" msgstr "コマンドグループ" @@ -1013,31 +1048,37 @@ msgstr "テストゲートウェイ" msgid "Gather facts" msgstr "資産情報の収集" -#: assets/const/category.py:11 assets/models/asset/host.py:8 +#: assets/const/category.py:10 assets/models/asset/host.py:8 #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: terminal/models/component/endpoint.py:13 terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "ホスト" -#: assets/const/category.py:12 assets/models/asset/device.py:8 +#: assets/const/category.py:11 assets/models/asset/device.py:8 msgid "Device" msgstr "インターネット機器" -#: assets/const/category.py:13 assets/models/asset/database.py:9 -#: assets/models/asset/database.py:24 assets/serializers/asset/common.py:119 +#: assets/const/category.py:12 assets/models/asset/database.py:9 +#: assets/models/asset/database.py:24 assets/serializers/asset/common.py:91 msgid "Database" msgstr "データベース" -#: assets/const/category.py:14 +#: assets/const/category.py:13 msgid "Cloud service" msgstr "クラウド サービス" -#: assets/const/category.py:15 assets/models/asset/web.py:16 audits/const.py:33 +#: assets/const/category.py:14 assets/models/asset/web.py:16 audits/const.py:33 #: terminal/models/applet/applet.py:25 msgid "Web" msgstr "Web" +#: assets/const/category.py:15 +#, fuzzy +#| msgid "Custom user" +msgid "Custom type" +msgstr "カスタムユーザー" + #: assets/const/cloud.py:7 msgid "Public cloud" msgstr "パブリック クラウド" @@ -1071,7 +1112,7 @@ msgstr "ファイアウォール" msgid "Other" msgstr "その他" -#: assets/const/types.py:200 +#: assets/const/types.py:214 msgid "All types" msgstr "いろんなタイプ" @@ -1089,7 +1130,7 @@ msgid "Basic" msgstr "基本" #: assets/const/web.py:61 assets/models/asset/web.py:13 -#: assets/serializers/asset/common.py:127 assets/serializers/platform.py:39 +#: assets/serializers/asset/common.py:99 assets/serializers/platform.py:40 msgid "Script" msgstr "脚本" @@ -1109,7 +1150,7 @@ msgstr "SSHパブリックキー" #: assets/models/cmd_filter.py:88 assets/models/group.py:23 #: common/db/models.py:37 ops/models/adhoc.py:27 ops/models/job.py:110 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:158 +#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:160 #: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 @@ -1158,7 +1199,7 @@ msgid "Username same with user" msgstr "ユーザーと同じユーザー名" #: assets/models/_user.py:52 authentication/models/connection_token.py:38 -#: authentication/serializers/connect_token_secret.py:104 +#: authentication/serializers/connect_token_secret.py:102 #: terminal/models/applet/applet.py:34 terminal/serializers/session.py:20 #: terminal/serializers/session.py:41 terminal/serializers/storage.py:68 msgid "Protocol" @@ -1218,19 +1259,19 @@ msgstr "クラウド サービス" msgid "Port" msgstr "ポート" -#: assets/models/asset/common.py:103 assets/serializers/asset/common.py:156 +#: assets/models/asset/common.py:103 assets/serializers/asset/common.py:129 msgid "Address" msgstr "アドレス" -#: assets/models/asset/common.py:104 assets/models/platform.py:95 -#: authentication/serializers/connect_token_secret.py:108 +#: assets/models/asset/common.py:104 assets/models/platform.py:100 +#: authentication/serializers/connect_token_secret.py:106 #: perms/serializers/user_permission.py:24 -#: xpack/plugins/cloud/serializers/account_attrs.py:197 +#: xpack/plugins/cloud/serializers/account_attrs.py:187 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:106 assets/models/domain.py:21 -#: authentication/serializers/connect_token_secret.py:126 +#: authentication/serializers/connect_token_secret.py:124 #: perms/serializers/user_permission.py:28 msgid "Domain" msgstr "ドメイン" @@ -1239,27 +1280,42 @@ msgstr "ドメイン" msgid "Labels" msgstr "ラベル" -#: assets/models/asset/common.py:111 assets/serializers/asset/host.py:27 -msgid "Info" -msgstr "情報" +#: assets/models/asset/common.py:111 +#, fuzzy +#| msgid "Gather asset hardware info" +msgid "Gathered info" +msgstr "資産ハードウェア情報の収集" -#: assets/models/asset/common.py:264 +#: assets/models/asset/common.py:112 assets/serializers/asset/common.py:109 +#: assets/serializers/asset/common.py:179 +#, fuzzy +#| msgid "Auto info" +msgid "Custom info" +msgstr "自動情報" + +#: assets/models/asset/common.py:271 msgid "Can refresh asset hardware info" msgstr "資産ハードウェア情報を更新できます" -#: assets/models/asset/common.py:265 +#: assets/models/asset/common.py:272 msgid "Can test asset connectivity" msgstr "資産接続をテストできます" -#: assets/models/asset/common.py:266 +#: assets/models/asset/common.py:273 msgid "Can match asset" msgstr "アセットを一致させることができます" -#: assets/models/asset/common.py:267 +#: assets/models/asset/common.py:274 msgid "Can change asset nodes" msgstr "資産ノードを変更できます" -#: assets/models/asset/database.py:10 assets/serializers/asset/common.py:120 +#: assets/models/asset/custom.py:8 +#, fuzzy +#| msgid "Custom user" +msgid "Custom asset" +msgstr "カスタムユーザー" + +#: assets/models/asset/database.py:10 assets/serializers/asset/common.py:92 #: settings/serializers/email.py:37 msgid "Use SSL" msgstr "SSLの使用" @@ -1276,31 +1332,31 @@ msgstr "クライアント証明書" msgid "Client key" msgstr "クライアントキー" -#: assets/models/asset/database.py:14 assets/serializers/asset/common.py:121 +#: assets/models/asset/database.py:14 assets/serializers/asset/common.py:93 msgid "Allow invalid cert" msgstr "証明書チェックを無視" -#: assets/models/asset/web.py:9 assets/serializers/platform.py:29 +#: assets/models/asset/web.py:9 assets/serializers/platform.py:30 msgid "Autofill" msgstr "自動充填" -#: assets/models/asset/web.py:10 assets/serializers/asset/common.py:124 -#: assets/serializers/platform.py:31 +#: assets/models/asset/web.py:10 assets/serializers/asset/common.py:96 +#: assets/serializers/platform.py:32 msgid "Username selector" msgstr "ユーザー名ピッカー" -#: assets/models/asset/web.py:11 assets/serializers/asset/common.py:125 -#: assets/serializers/platform.py:34 +#: assets/models/asset/web.py:11 assets/serializers/asset/common.py:97 +#: assets/serializers/platform.py:35 msgid "Password selector" msgstr "パスワードセレクター" -#: assets/models/asset/web.py:12 assets/serializers/asset/common.py:126 -#: assets/serializers/platform.py:37 +#: assets/models/asset/web.py:12 assets/serializers/asset/common.py:98 +#: assets/serializers/platform.py:38 msgid "Submit selector" msgstr "ボタンセレクターを確認する" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:306 rbac/tree.py:35 +#: assets/serializers/asset/common.py:297 rbac/tree.py:35 msgid "Accounts" msgstr "アカウント" @@ -1314,7 +1370,7 @@ msgstr "アセットの自動化タスク" #: assets/models/automations/base.py:112 audits/models.py:177 #: audits/serializers.py:49 ops/models/base.py:49 ops/models/job.py:183 -#: terminal/models/applet/applet.py:157 terminal/models/applet/host.py:108 +#: terminal/models/applet/applet.py:159 terminal/models/applet/host.py:108 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:103 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1382,6 +1438,7 @@ msgid "Asset group" msgstr "資産グループ" #: assets/models/group.py:34 assets/models/platform.py:17 +#: assets/serializers/platform.py:97 #: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "デフォルト" @@ -1397,14 +1454,15 @@ msgstr "システム" #: assets/models/label.py:19 assets/models/node.py:544 #: assets/serializers/cagegory.py:7 assets/serializers/cagegory.py:14 #: authentication/models/connection_token.py:26 -#: authentication/serializers/connect_token_secret.py:115 +#: authentication/serializers/connect_token_secret.py:113 #: common/serializers/common.py:80 settings/models.py:34 msgid "Value" msgstr "値" -#: assets/models/label.py:40 assets/serializers/asset/common.py:133 +#: assets/models/label.py:40 assets/serializers/asset/common.py:105 #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 -#: authentication/serializers/connect_token_secret.py:114 +#: assets/serializers/platform.py:94 +#: authentication/serializers/connect_token_secret.py:112 #: common/serializers/common.py:79 settings/serializers/sms.py:7 msgid "Label" msgstr "ラベル" @@ -1446,90 +1504,102 @@ msgstr "主要" msgid "Required" msgstr "必要" -#: assets/models/platform.py:18 settings/serializers/settings.py:65 +#: assets/models/platform.py:18 +#, fuzzy +#| msgid "Public IP" +msgid "Public" +msgstr "パブリックIP" + +#: assets/models/platform.py:19 settings/serializers/settings.py:65 #: users/templates/users/reset_password.html:29 msgid "Setting" msgstr "設定" -#: assets/models/platform.py:30 audits/const.py:47 settings/models.py:37 +#: assets/models/platform.py:31 audits/const.py:47 settings/models.py:37 #: terminal/serializers/applet_host.py:29 msgid "Enabled" msgstr "有効化" -#: assets/models/platform.py:31 +#: assets/models/platform.py:32 msgid "Ansible config" msgstr "Ansible 構成" -#: assets/models/platform.py:32 assets/serializers/platform.py:60 +#: assets/models/platform.py:33 assets/serializers/platform.py:60 msgid "Ping enabled" msgstr "アセット ディスカバリを有効にする" -#: assets/models/platform.py:33 assets/serializers/platform.py:61 +#: assets/models/platform.py:34 assets/serializers/platform.py:61 msgid "Ping method" msgstr "資産検出方法" -#: assets/models/platform.py:34 assets/models/platform.py:47 +#: assets/models/platform.py:35 assets/models/platform.py:48 #: assets/serializers/platform.py:62 msgid "Gather facts enabled" msgstr "資産情報の収集を有効にする" -#: assets/models/platform.py:35 assets/models/platform.py:49 +#: assets/models/platform.py:36 assets/models/platform.py:50 #: assets/serializers/platform.py:63 msgid "Gather facts method" msgstr "情報収集の方法" -#: assets/models/platform.py:36 assets/serializers/platform.py:66 +#: assets/models/platform.py:37 assets/serializers/platform.py:66 msgid "Change secret enabled" msgstr "パスワードの変更が有効" -#: assets/models/platform.py:38 assets/serializers/platform.py:67 +#: assets/models/platform.py:39 assets/serializers/platform.py:67 msgid "Change secret method" msgstr "パスワード変更モード" -#: assets/models/platform.py:40 assets/serializers/platform.py:68 +#: assets/models/platform.py:41 assets/serializers/platform.py:68 msgid "Push account enabled" msgstr "アカウントのプッシュを有効にする" -#: assets/models/platform.py:42 assets/serializers/platform.py:69 +#: assets/models/platform.py:43 assets/serializers/platform.py:69 msgid "Push account method" msgstr "アカウントプッシュ方式" -#: assets/models/platform.py:44 assets/serializers/platform.py:64 +#: assets/models/platform.py:45 assets/serializers/platform.py:64 msgid "Verify account enabled" msgstr "アカウントの確認をオンにする" -#: assets/models/platform.py:46 assets/serializers/platform.py:65 +#: assets/models/platform.py:47 assets/serializers/platform.py:65 msgid "Verify account method" msgstr "アカウント認証方法" -#: assets/models/platform.py:67 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:68 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "メタ" -#: assets/models/platform.py:68 +#: assets/models/platform.py:69 msgid "Internal" msgstr "ビルトイン" -#: assets/models/platform.py:71 assets/serializers/platform.py:97 +#: assets/models/platform.py:73 assets/serializers/platform.py:109 msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:73 assets/serializers/platform.py:124 +#: assets/models/platform.py:75 assets/serializers/platform.py:133 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:75 assets/serializers/platform.py:123 +#: assets/models/platform.py:77 assets/serializers/platform.py:132 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" -#: assets/models/platform.py:76 assets/serializers/platform.py:106 +#: assets/models/platform.py:78 assets/serializers/platform.py:115 msgid "Su method" msgstr "アカウントの切り替え方法" -#: assets/models/platform.py:78 assets/serializers/platform.py:104 +#: assets/models/platform.py:81 assets/serializers/platform.py:113 msgid "Automation" msgstr "オートメーション" +#: assets/models/platform.py:83 assets/serializers/platform.py:118 +#, fuzzy +#| msgid "Custom user" +msgid "Custom fields" +msgstr "カスタムユーザー" + #: assets/models/utils.py:18 #, python-format msgid "%(value)s is not an even number" @@ -1543,36 +1613,36 @@ msgstr "" "プラットフォームタイプがスキップされた資産に合致しない、資産内の一括更新プ" "ラットフォーム" -#: assets/serializers/asset/common.py:123 +#: assets/serializers/asset/common.py:95 msgid "Auto fill" msgstr "自動充填" -#: assets/serializers/asset/common.py:134 assets/serializers/platform.py:102 -#: authentication/serializers/connect_token_secret.py:28 -#: authentication/serializers/connect_token_secret.py:66 +#: assets/serializers/asset/common.py:106 assets/serializers/platform.py:112 +#: authentication/serializers/connect_token_secret.py:29 +#: authentication/serializers/connect_token_secret.py:63 #: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99 msgid "Protocols" msgstr "プロトコル" -#: assets/serializers/asset/common.py:136 -#: assets/serializers/asset/common.py:157 +#: assets/serializers/asset/common.py:108 +#: assets/serializers/asset/common.py:130 msgid "Node path" msgstr "ノードパスです" -#: assets/serializers/asset/common.py:154 -#: assets/serializers/asset/common.py:308 +#: assets/serializers/asset/common.py:127 +#: assets/serializers/asset/common.py:299 msgid "Auto info" msgstr "自動情報" -#: assets/serializers/asset/common.py:232 +#: assets/serializers/asset/common.py:220 msgid "Platform not exist" msgstr "プラットフォームが存在しません" -#: assets/serializers/asset/common.py:267 +#: assets/serializers/asset/common.py:255 msgid "port out of range (1-65535)" msgstr "ポート番号が範囲外です (1-65535)" -#: assets/serializers/asset/common.py:274 +#: assets/serializers/asset/common.py:262 msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" @@ -1621,7 +1691,7 @@ msgid "Disk total" msgstr "ディスクの合計" #: assets/serializers/asset/host.py:21 -#: authentication/serializers/connect_token_secret.py:105 +#: authentication/serializers/connect_token_secret.py:103 msgid "OS" msgstr "OS" @@ -1633,6 +1703,10 @@ msgstr "システムバージョン" msgid "OS arch" msgstr "システムアーキテクチャ" +#: assets/serializers/asset/host.py:27 +msgid "Info" +msgstr "情報" + #: assets/serializers/cagegory.py:9 msgid "Constraints" msgstr "制約" @@ -1661,11 +1735,11 @@ msgstr "含まれない:/" msgid "The same level node name cannot be the same" msgstr "同じレベルのノード名を同じにすることはできません。" -#: assets/serializers/platform.py:25 +#: assets/serializers/platform.py:26 msgid "SFTP enabled" msgstr "SFTP が有効" -#: assets/serializers/platform.py:26 +#: assets/serializers/platform.py:27 msgid "SFTP home" msgstr "SFTP ルート パス" @@ -1681,11 +1755,17 @@ msgstr "アカウント収集を有効にする" msgid "Gather accounts method" msgstr "アカウントの収集方法" -#: assets/serializers/platform.py:125 +#: assets/serializers/platform.py:134 msgid "Default Domain" msgstr "デフォルト ドメイン" -#: assets/serializers/platform.py:137 +#: assets/serializers/platform.py:143 +#, fuzzy +#| msgid "test_phone is required" +msgid "type is required" +msgstr "携帯番号をテストこのフィールドは必須です" + +#: assets/serializers/platform.py:173 #, fuzzy #| msgid "Protocol is required: {}" msgid "Protocols is required" @@ -1826,7 +1906,7 @@ msgid "Change password" msgstr "パスワードを変更する" #: audits/const.py:34 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:161 +#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:163 #: terminal/serializers/session.py:48 msgid "Terminal" msgstr "ターミナル" @@ -2501,23 +2581,23 @@ msgstr "異なる都市ログインのリマインダー" msgid "binding reminder" msgstr "バインディングリマインダー" -#: authentication/serializers/connect_token_secret.py:106 +#: authentication/serializers/connect_token_secret.py:104 msgid "Is builtin" msgstr "ビルトイン" -#: authentication/serializers/connect_token_secret.py:110 +#: authentication/serializers/connect_token_secret.py:108 msgid "Options" msgstr "オプション" -#: authentication/serializers/connect_token_secret.py:117 +#: authentication/serializers/connect_token_secret.py:115 msgid "Component" msgstr "コンポーネント" -#: authentication/serializers/connect_token_secret.py:128 +#: authentication/serializers/connect_token_secret.py:126 msgid "Expired now" msgstr "すぐに期限切れ" -#: authentication/serializers/connect_token_secret.py:148 +#: authentication/serializers/connect_token_secret.py:145 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" @@ -3420,6 +3500,10 @@ msgstr "アルグ" msgid "Creator" msgstr "作成者" +#: ops/models/base.py:19 +msgid "Account policy" +msgstr "アカウント ポリシー" + #: ops/models/base.py:20 msgid "Last execution" msgstr "最後の実行" @@ -3453,13 +3537,6 @@ msgstr "タスクモニターを表示できます" msgid "Kwargs" msgstr "クワーグ" -#: ops/models/celery.py:60 tickets/models/comment.py:13 -#: tickets/models/ticket/general.py:45 tickets/models/ticket/general.py:279 -#: tickets/serializers/super_ticket.py:14 -#: tickets/serializers/ticket/ticket.py:21 -msgid "State" -msgstr "状態" - #: ops/models/celery.py:61 terminal/models/session/sharing.py:114 #: tickets/const.py:25 msgid "Finished" @@ -3973,7 +4050,7 @@ msgid "My assets" msgstr "私の資産" #: rbac/tree.py:56 terminal/models/applet/applet.py:43 -#: terminal/models/applet/applet.py:154 terminal/models/applet/host.py:28 +#: terminal/models/applet/applet.py:156 terminal/models/applet/host.py:28 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" @@ -4473,7 +4550,7 @@ msgid "SSO auth key TTL" msgstr "Token有効期間" #: settings/serializers/auth/sso.py:17 -#: xpack/plugins/cloud/serializers/account_attrs.py:194 +#: xpack/plugins/cloud/serializers/account_attrs.py:184 msgid "Unit: second" msgstr "単位: 秒" @@ -5063,11 +5140,11 @@ msgstr "" "ヒント: Luna ページでグラフィック アセットを接続するときに使用するデフォルト" "の解像度" -#: settings/tasks/ldap.py:24 +#: settings/tasks/ldap.py:25 msgid "Import ldap user" msgstr "LDAP ユーザーのインポート" -#: settings/tasks/ldap.py:45 +#: settings/tasks/ldap.py:47 msgid "Periodic import ldap user" msgstr "LDAP ユーザーを定期的にインポートする" @@ -5258,8 +5335,8 @@ msgstr "期限切れです。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" @@ -5280,34 +5357,34 @@ msgid "" " " msgstr "" "\n" -" クリックしてください リンク パスワードの更新\n" +" クリックしてください リンク パスワードの更新\n" " " #: templates/_message.html:43 #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" -" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" +" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" " " #: templates/_message.html:56 #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" +" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -5523,7 +5600,7 @@ msgstr "ホスト" msgid "Applet pkg not valid, Missing file {}" msgstr "無効なアプレット パッケージ、ファイル {} がありません" -#: terminal/models/applet/applet.py:156 terminal/models/applet/host.py:34 +#: terminal/models/applet/applet.py:158 terminal/models/applet/host.py:34 #: terminal/models/applet/host.py:106 msgid "Hosting" msgstr "ホスト マシン" @@ -5629,28 +5706,28 @@ msgid "Default storage" msgstr "デフォルトのストレージ" #: terminal/models/component/storage.py:140 -#: terminal/models/component/terminal.py:90 +#: terminal/models/component/terminal.py:91 msgid "Command storage" msgstr "コマンドストレージ" #: terminal/models/component/storage.py:200 -#: terminal/models/component/terminal.py:91 +#: terminal/models/component/terminal.py:92 msgid "Replay storage" msgstr "再生ストレージ" -#: terminal/models/component/terminal.py:87 +#: terminal/models/component/terminal.py:88 msgid "type" msgstr "タイプ" -#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:51 +#: terminal/models/component/terminal.py:90 terminal/serializers/command.py:51 msgid "Remote Address" msgstr "リモートアドレス" -#: terminal/models/component/terminal.py:92 +#: terminal/models/component/terminal.py:93 msgid "Application User" msgstr "ユーザーの適用" -#: terminal/models/component/terminal.py:163 +#: terminal/models/component/terminal.py:165 msgid "Can view terminal config" msgstr "ターミナル構成を表示できます" @@ -7359,13 +7436,13 @@ msgstr "ファイルはJSON形式です。" msgid "IP address invalid `{}`, {}" msgstr "IPアドレスが無効: '{}', {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:162 msgid "" "Format for comma-delimited string,Such as: 192.168.1.0/24, " "10.0.0.0-10.0.0.255" msgstr "形式はコンマ区切りの文字列です,例:192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:176 +#: xpack/plugins/cloud/serializers/account_attrs.py:166 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -7375,19 +7452,19 @@ msgstr "" "実行されると、有効な IP アドレスのみが同期されます。
ポートが0の場合、す" "べてのIPアドレスが有効です。" -#: xpack/plugins/cloud/serializers/account_attrs.py:184 +#: xpack/plugins/cloud/serializers/account_attrs.py:174 msgid "Hostname prefix" msgstr "ホスト名プレフィックス" -#: xpack/plugins/cloud/serializers/account_attrs.py:187 +#: xpack/plugins/cloud/serializers/account_attrs.py:177 msgid "IP segment" msgstr "IP セグメント" -#: xpack/plugins/cloud/serializers/account_attrs.py:191 +#: xpack/plugins/cloud/serializers/account_attrs.py:181 msgid "Test port" msgstr "テストポート" -#: xpack/plugins/cloud/serializers/account_attrs.py:194 +#: xpack/plugins/cloud/serializers/account_attrs.py:184 msgid "Test timeout" msgstr "テストタイムアウト" @@ -7469,22 +7546,27 @@ msgstr "ライセンスのインポートに成功" msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:138 +#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:135 msgid "License" msgstr "ライセンス" -#: xpack/plugins/license/models.py:80 +#: xpack/plugins/license/models.py:79 msgid "Standard edition" msgstr "標準版" -#: xpack/plugins/license/models.py:82 +#: xpack/plugins/license/models.py:81 msgid "Enterprise edition" msgstr "エンタープライズ版" -#: xpack/plugins/license/models.py:84 +#: xpack/plugins/license/models.py:83 msgid "Ultimate edition" msgstr "究極のエディション" -#: xpack/plugins/license/models.py:86 +#: xpack/plugins/license/models.py:85 msgid "Community edition" msgstr "コミュニティ版" + +#, fuzzy +#~| msgid "Custom user" +#~ msgid "Custom" +#~ msgstr "カスタムユーザー" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 7cf7ca157..3dbd987da 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43695645a64669ba25c4fdfd413ce497a07592c320071b399cbb4f54466441e3 -size 113361 +oid sha256:035f9429613b541f229855a7d36c98e5f4736efce54dcd21119660dd6d89d94e +size 114269 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 95a5402be..29286bd92 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-03 18:06+0800\n" +"POT-Creation-Date: 2023-04-07 13:57+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -175,12 +175,13 @@ msgstr "仅创建" #: accounts/models/account.py:47 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:110 +#: accounts/serializers/account/account.py:173 +#: accounts/serializers/account/account.py:206 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/models/base.py:100 acls/serializers/base.py:56 -#: assets/models/asset/common.py:92 assets/models/asset/common.py:261 +#: assets/models/asset/common.py:92 assets/models/asset/common.py:268 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 #: assets/serializers/label.py:27 audits/models.py:48 #: authentication/models/connection_token.py:33 @@ -191,8 +192,8 @@ msgstr "仅创建" msgid "Asset" msgstr "资产" -#: accounts/models/account.py:51 accounts/serializers/account/account.py:114 -#: authentication/serializers/connect_token_secret.py:50 +#: accounts/models/account.py:51 accounts/serializers/account/account.py:178 +#: authentication/serializers/connect_token_secret.py:48 msgid "Su from" msgstr "切换自" @@ -201,16 +202,20 @@ msgstr "切换自" msgid "Version" msgstr "版本" -#: accounts/models/account.py:55 accounts/serializers/account/account.py:111 +#: accounts/models/account.py:55 accounts/serializers/account/account.py:174 #: users/models/user.py:768 msgid "Source" msgstr "来源" -#: accounts/models/account.py:58 +#: accounts/models/account.py:56 +msgid "Source ID" +msgstr "来源 ID" + +#: accounts/models/account.py:59 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 #: acls/models/base.py:102 acls/serializers/base.py:57 -#: assets/serializers/asset/common.py:135 assets/serializers/gateway.py:28 +#: assets/serializers/asset/common.py:107 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:21 terminal/models/session/session.py:34 @@ -218,35 +223,35 @@ msgstr "来源" msgid "Account" msgstr "账号" -#: accounts/models/account.py:64 +#: accounts/models/account.py:65 msgid "Can view asset account secret" msgstr "可以查看资产账号密码" -#: accounts/models/account.py:65 +#: accounts/models/account.py:66 msgid "Can view asset history account" msgstr "可以查看资产历史账号" -#: accounts/models/account.py:66 +#: accounts/models/account.py:67 msgid "Can view asset history account secret" msgstr "可以查看资产历史账号密码" -#: accounts/models/account.py:67 +#: accounts/models/account.py:68 msgid "Can verify account" msgstr "可以验证账号" -#: accounts/models/account.py:68 +#: accounts/models/account.py:69 msgid "Can push account" msgstr "可以推送账号" -#: accounts/models/account.py:109 +#: accounts/models/account.py:110 msgid "Account template" msgstr "账号模版" -#: accounts/models/account.py:114 +#: accounts/models/account.py:115 msgid "Can view asset account template secret" msgstr "可以查看资产账号模版密码" -#: accounts/models/account.py:115 +#: accounts/models/account.py:116 msgid "Can change asset account template secret" msgstr "可以更改资产账号模版密码" @@ -343,12 +348,11 @@ msgid "Can add push account execution" msgstr "创建推送账号执行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:175 +#: accounts/serializers/account/account.py:383 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 -#: assets/serializers/asset/common.py:77 -#: authentication/serializers/connect_token_secret.py:41 -#: authentication/serializers/connect_token_secret.py:51 +#: authentication/serializers/connect_token_secret.py:40 +#: authentication/serializers/connect_token_secret.py:49 msgid "Secret type" msgstr "密文类型" @@ -393,7 +397,8 @@ msgstr "开始日期" msgid "Date finished" msgstr "结束日期" -#: accounts/models/automations/change_secret.py:94 assets/const/automation.py:8 +#: accounts/models/automations/change_secret.py:94 +#: accounts/serializers/account/account.py:208 assets/const/automation.py:8 #: common/const/choices.py:20 msgid "Error" msgstr "错误" @@ -432,10 +437,8 @@ msgid "Gather account automation" msgstr "自动化收集账号" #: accounts/models/automations/gather_account.py:56 -#, fuzzy -#| msgid "Is service account" msgid "Is sync account" -msgstr "服务账号" +msgstr "是否同步账号" #: accounts/models/automations/gather_account.py:71 #: accounts/tasks/gather_accounts.py:29 @@ -449,7 +452,7 @@ msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 #: acls/serializers/base.py:81 acls/serializers/login_acl.py:25 #: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 -#: authentication/serializers/connect_token_secret.py:109 +#: authentication/serializers/connect_token_secret.py:107 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" msgstr "动作" @@ -468,10 +471,10 @@ msgstr "账号验证" #: assets/models/asset/common.py:90 assets/models/asset/common.py:102 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 -#: assets/models/platform.py:13 assets/models/platform.py:64 -#: assets/serializers/asset/common.py:74 assets/serializers/asset/common.py:155 -#: assets/serializers/platform.py:148 -#: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21 +#: assets/models/platform.py:13 assets/models/platform.py:65 +#: assets/serializers/asset/common.py:128 assets/serializers/platform.py:93 +#: assets/serializers/platform.py:184 +#: authentication/serializers/connect_token_secret.py:101 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:91 ops/models/playbook.py:23 ops/serializers/job.py:19 #: orgs/models.py:69 perms/models/asset_permission.py:56 rbac/models/role.py:29 @@ -479,7 +482,7 @@ msgstr "账号验证" #: terminal/models/applet/applet.py:27 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:90 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:15 -#: terminal/models/component/terminal.py:84 users/forms/profile.py:33 +#: terminal/models/component/terminal.py:85 users/forms/profile.py:33 #: users/models/group.py:13 users/models/user.py:717 #: xpack/plugins/cloud/models.py:28 msgid "Name" @@ -492,7 +495,7 @@ msgstr "特权账号" #: accounts/models/base.py:40 assets/models/asset/common.py:109 #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 -#: authentication/serializers/connect_token_secret.py:107 +#: authentication/serializers/connect_token_secret.py:105 #: terminal/models/applet/applet.py:32 users/serializers/user.py:162 msgid "Is active" msgstr "激活" @@ -535,38 +538,36 @@ msgstr "" "{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加" "密密码" -#: accounts/serializers/account/account.py:80 -#: assets/serializers/asset/common.py:72 settings/serializers/auth/sms.py:75 +#: accounts/serializers/account/account.py:26 +#: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "模板" -#: accounts/serializers/account/account.py:83 -#: assets/serializers/asset/common.py:69 +#: accounts/serializers/account/account.py:29 msgid "Push now" msgstr "立即推送" -#: accounts/serializers/account/account.py:85 -#: accounts/serializers/account/base.py:64 -msgid "Has secret" -msgstr "已托管密码" +#: accounts/serializers/account/account.py:33 +msgid "Exist policy" +msgstr "账号存在策略" -#: accounts/serializers/account/account.py:90 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:65 -#: assets/serializers/asset/common.py:131 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:100 assets/serializers/platform.py:149 +#: accounts/serializers/account/account.py:153 applications/models.py:11 +#: assets/models/label.py:21 assets/models/platform.py:66 +#: assets/serializers/asset/common.py:103 assets/serializers/cagegory.py:8 +#: assets/serializers/platform.py:111 assets/serializers/platform.py:185 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:91 +#: accounts/serializers/account/account.py:154 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:18 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:66 -#: assets/serializers/asset/common.py:132 assets/serializers/platform.py:99 -#: audits/serializers.py:48 -#: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:102 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:67 +#: assets/serializers/asset/common.py:104 assets/serializers/platform.py:95 +#: assets/serializers/platform.py:110 audits/serializers.py:48 +#: authentication/serializers/connect_token_secret.py:114 ops/models/job.py:102 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -578,15 +579,49 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:106 +#: accounts/serializers/account/account.py:169 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:118 ops/models/base.py:19 -msgid "Account policy" -msgstr "账号策略" +#: accounts/serializers/account/account.py:175 +#: accounts/serializers/account/base.py:64 +msgid "Has secret" +msgstr "已托管密码" -#: accounts/serializers/account/account.py:185 acls/models/base.py:98 +#: accounts/serializers/account/account.py:207 ops/models/celery.py:60 +#: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 +#: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 +#: tickets/serializers/ticket/ticket.py:21 +msgid "State" +msgstr "状态" + +#: accounts/serializers/account/account.py:209 +msgid "Changed" +msgstr "已修改" + +#: accounts/serializers/account/account.py:213 +#: accounts/serializers/automations/base.py:22 +#: assets/models/automations/base.py:19 +#: assets/serializers/automations/base.py:20 ops/models/base.py:17 +#: ops/models/job.py:104 ops/serializers/job.py:20 +#: terminal/templates/terminal/_msg_command_execute_alert.html:16 +msgid "Assets" +msgstr "资产" + +#: accounts/serializers/account/account.py:284 +msgid "Account already exists" +msgstr "账号已存在" + +#: accounts/serializers/account/account.py:320 +#, python-format +msgid "Asset does not support this secret type: %s" +msgstr "资产不支持账号类型: %s" + +#: accounts/serializers/account/account.py:351 +msgid "Account has exist" +msgstr "账号已存在" + +#: accounts/serializers/account/account.py:393 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:55 #: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 @@ -604,7 +639,7 @@ msgstr "账号策略" msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:186 +#: accounts/serializers/account/account.py:394 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -635,19 +670,11 @@ msgstr "资产类型" msgid "Key password" msgstr "密钥密码" -#: accounts/serializers/account/base.py:81 -#: assets/serializers/asset/common.py:307 +#: accounts/serializers/account/base.py:80 +#: assets/serializers/asset/common.py:298 msgid "Spec info" msgstr "特殊信息" -#: accounts/serializers/automations/base.py:22 -#: assets/models/automations/base.py:19 -#: assets/serializers/automations/base.py:20 ops/models/base.py:17 -#: ops/models/job.py:104 ops/serializers/job.py:20 -#: terminal/templates/terminal/_msg_command_execute_alert.html:16 -msgid "Assets" -msgstr "资产" - #: accounts/serializers/automations/base.py:23 #: assets/models/asset/common.py:108 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 @@ -730,11 +757,6 @@ msgstr "密码不能包含 `\"` 字符" msgid "private key invalid or passphrase error" msgstr "密钥不合法或密钥密码错误" -#: accounts/validator.py:41 -#, python-brace-format -msgid "{field_name} not a legal option" -msgstr "" - #: acls/apps.py:7 msgid "Acls" msgstr "访问控制" @@ -764,7 +786,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)" #: acls/models/base.py:82 acls/serializers/base.py:75 #: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 -#: authentication/serializers/connect_token_secret.py:82 +#: authentication/serializers/connect_token_secret.py:79 msgid "Reviewers" msgstr "审批人" @@ -789,7 +811,7 @@ msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/basic.py:10 xpack/plugins/license/models.py:30 +#: settings/serializers/basic.py:10 xpack/plugins/license/models.py:29 msgid "Content" msgstr "内容" @@ -803,7 +825,7 @@ msgstr "忽略大小写" #: acls/models/command_acl.py:33 acls/models/command_acl.py:96 #: acls/serializers/command_acl.py:28 -#: authentication/serializers/connect_token_secret.py:79 +#: authentication/serializers/connect_token_secret.py:76 msgid "Command group" msgstr "命令组" @@ -1005,31 +1027,35 @@ msgstr "测试网关" msgid "Gather facts" msgstr "收集资产信息" -#: assets/const/category.py:11 assets/models/asset/host.py:8 +#: assets/const/category.py:10 assets/models/asset/host.py:8 #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: terminal/models/component/endpoint.py:13 terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "主机" -#: assets/const/category.py:12 assets/models/asset/device.py:8 +#: assets/const/category.py:11 assets/models/asset/device.py:8 msgid "Device" msgstr "网络设备" -#: assets/const/category.py:13 assets/models/asset/database.py:9 -#: assets/models/asset/database.py:24 assets/serializers/asset/common.py:119 +#: assets/const/category.py:12 assets/models/asset/database.py:9 +#: assets/models/asset/database.py:24 assets/serializers/asset/common.py:91 msgid "Database" msgstr "数据库" -#: assets/const/category.py:14 +#: assets/const/category.py:13 msgid "Cloud service" msgstr "云服务" -#: assets/const/category.py:15 assets/models/asset/web.py:16 audits/const.py:33 +#: assets/const/category.py:14 assets/models/asset/web.py:16 audits/const.py:33 #: terminal/models/applet/applet.py:25 msgid "Web" msgstr "Web" +#: assets/const/category.py:15 +msgid "Custom type" +msgstr "自定义类型" + #: assets/const/cloud.py:7 msgid "Public cloud" msgstr "公有云" @@ -1063,7 +1089,7 @@ msgstr "防火墙" msgid "Other" msgstr "其它" -#: assets/const/types.py:200 +#: assets/const/types.py:214 msgid "All types" msgstr "所有类型" @@ -1081,7 +1107,7 @@ msgid "Basic" msgstr "基本" #: assets/const/web.py:61 assets/models/asset/web.py:13 -#: assets/serializers/asset/common.py:127 assets/serializers/platform.py:39 +#: assets/serializers/asset/common.py:99 assets/serializers/platform.py:40 msgid "Script" msgstr "脚本" @@ -1101,7 +1127,7 @@ msgstr "SSH公钥" #: assets/models/cmd_filter.py:88 assets/models/group.py:23 #: common/db/models.py:37 ops/models/adhoc.py:27 ops/models/job.py:110 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:158 +#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:160 #: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 @@ -1150,7 +1176,7 @@ msgid "Username same with user" msgstr "用户名与用户相同" #: assets/models/_user.py:52 authentication/models/connection_token.py:38 -#: authentication/serializers/connect_token_secret.py:104 +#: authentication/serializers/connect_token_secret.py:102 #: terminal/models/applet/applet.py:34 terminal/serializers/session.py:20 #: terminal/serializers/session.py:41 terminal/serializers/storage.py:68 msgid "Protocol" @@ -1210,19 +1236,19 @@ msgstr "云服务" msgid "Port" msgstr "端口" -#: assets/models/asset/common.py:103 assets/serializers/asset/common.py:156 +#: assets/models/asset/common.py:103 assets/serializers/asset/common.py:129 msgid "Address" msgstr "地址" -#: assets/models/asset/common.py:104 assets/models/platform.py:95 -#: authentication/serializers/connect_token_secret.py:108 +#: assets/models/asset/common.py:104 assets/models/platform.py:100 +#: authentication/serializers/connect_token_secret.py:106 #: perms/serializers/user_permission.py:24 -#: xpack/plugins/cloud/serializers/account_attrs.py:197 +#: xpack/plugins/cloud/serializers/account_attrs.py:187 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:106 assets/models/domain.py:21 -#: authentication/serializers/connect_token_secret.py:126 +#: authentication/serializers/connect_token_secret.py:124 #: perms/serializers/user_permission.py:28 msgid "Domain" msgstr "网域" @@ -1231,27 +1257,40 @@ msgstr "网域" msgid "Labels" msgstr "标签管理" -#: assets/models/asset/common.py:111 assets/serializers/asset/host.py:27 -msgid "Info" -msgstr "信息" +#: assets/models/asset/common.py:111 +#, fuzzy +#| msgid "Gather asset hardware info" +msgid "Gathered info" +msgstr "收集资产硬件信息" -#: assets/models/asset/common.py:264 +#: assets/models/asset/common.py:112 assets/serializers/asset/common.py:109 +#: assets/serializers/asset/common.py:179 +#, fuzzy +#| msgid "Auto info" +msgid "Custom info" +msgstr "自动化信息" + +#: assets/models/asset/common.py:271 msgid "Can refresh asset hardware info" msgstr "可以更新资产硬件信息" -#: assets/models/asset/common.py:265 +#: assets/models/asset/common.py:272 msgid "Can test asset connectivity" msgstr "可以测试资产连接性" -#: assets/models/asset/common.py:266 +#: assets/models/asset/common.py:273 msgid "Can match asset" msgstr "可以匹配资产" -#: assets/models/asset/common.py:267 +#: assets/models/asset/common.py:274 msgid "Can change asset nodes" msgstr "可以修改资产节点" -#: assets/models/asset/database.py:10 assets/serializers/asset/common.py:120 +#: assets/models/asset/custom.py:8 +msgid "Custom asset" +msgstr "自定义资产" + +#: assets/models/asset/database.py:10 assets/serializers/asset/common.py:92 #: settings/serializers/email.py:37 msgid "Use SSL" msgstr "使用 SSL" @@ -1268,31 +1307,31 @@ msgstr "客户端证书" msgid "Client key" msgstr "客户端密钥" -#: assets/models/asset/database.py:14 assets/serializers/asset/common.py:121 +#: assets/models/asset/database.py:14 assets/serializers/asset/common.py:93 msgid "Allow invalid cert" msgstr "忽略证书校验" -#: assets/models/asset/web.py:9 assets/serializers/platform.py:29 +#: assets/models/asset/web.py:9 assets/serializers/platform.py:30 msgid "Autofill" msgstr "自动代填" -#: assets/models/asset/web.py:10 assets/serializers/asset/common.py:124 -#: assets/serializers/platform.py:31 +#: assets/models/asset/web.py:10 assets/serializers/asset/common.py:96 +#: assets/serializers/platform.py:32 msgid "Username selector" msgstr "用户名选择器" -#: assets/models/asset/web.py:11 assets/serializers/asset/common.py:125 -#: assets/serializers/platform.py:34 +#: assets/models/asset/web.py:11 assets/serializers/asset/common.py:97 +#: assets/serializers/platform.py:35 msgid "Password selector" msgstr "密码选择器" -#: assets/models/asset/web.py:12 assets/serializers/asset/common.py:126 -#: assets/serializers/platform.py:37 +#: assets/models/asset/web.py:12 assets/serializers/asset/common.py:98 +#: assets/serializers/platform.py:38 msgid "Submit selector" msgstr "确认按钮选择器" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:306 rbac/tree.py:35 +#: assets/serializers/asset/common.py:297 rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" @@ -1306,7 +1345,7 @@ msgstr "资产自动化任务" #: assets/models/automations/base.py:112 audits/models.py:177 #: audits/serializers.py:49 ops/models/base.py:49 ops/models/job.py:183 -#: terminal/models/applet/applet.py:157 terminal/models/applet/host.py:108 +#: terminal/models/applet/applet.py:159 terminal/models/applet/host.py:108 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:103 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1374,6 +1413,7 @@ msgid "Asset group" msgstr "资产组" #: assets/models/group.py:34 assets/models/platform.py:17 +#: assets/serializers/platform.py:97 #: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "默认" @@ -1389,14 +1429,15 @@ msgstr "系统" #: assets/models/label.py:19 assets/models/node.py:544 #: assets/serializers/cagegory.py:7 assets/serializers/cagegory.py:14 #: authentication/models/connection_token.py:26 -#: authentication/serializers/connect_token_secret.py:115 +#: authentication/serializers/connect_token_secret.py:113 #: common/serializers/common.py:80 settings/models.py:34 msgid "Value" msgstr "值" -#: assets/models/label.py:40 assets/serializers/asset/common.py:133 +#: assets/models/label.py:40 assets/serializers/asset/common.py:105 #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 -#: authentication/serializers/connect_token_secret.py:114 +#: assets/serializers/platform.py:94 +#: authentication/serializers/connect_token_secret.py:112 #: common/serializers/common.py:79 settings/serializers/sms.py:7 msgid "Label" msgstr "标签" @@ -1438,90 +1479,98 @@ msgstr "主要的" msgid "Required" msgstr "必须的" -#: assets/models/platform.py:18 settings/serializers/settings.py:65 +#: assets/models/platform.py:18 +msgid "Public" +msgstr "开放的" + +#: assets/models/platform.py:19 settings/serializers/settings.py:65 #: users/templates/users/reset_password.html:29 msgid "Setting" msgstr "设置" -#: assets/models/platform.py:30 audits/const.py:47 settings/models.py:37 +#: assets/models/platform.py:31 audits/const.py:47 settings/models.py:37 #: terminal/serializers/applet_host.py:29 msgid "Enabled" msgstr "启用" -#: assets/models/platform.py:31 +#: assets/models/platform.py:32 msgid "Ansible config" msgstr "Ansible 配置" -#: assets/models/platform.py:32 assets/serializers/platform.py:60 +#: assets/models/platform.py:33 assets/serializers/platform.py:60 msgid "Ping enabled" msgstr "启用资产探活" -#: assets/models/platform.py:33 assets/serializers/platform.py:61 +#: assets/models/platform.py:34 assets/serializers/platform.py:61 msgid "Ping method" msgstr "资产探活方式" -#: assets/models/platform.py:34 assets/models/platform.py:47 +#: assets/models/platform.py:35 assets/models/platform.py:48 #: assets/serializers/platform.py:62 msgid "Gather facts enabled" msgstr "启用收集资产信息" -#: assets/models/platform.py:35 assets/models/platform.py:49 +#: assets/models/platform.py:36 assets/models/platform.py:50 #: assets/serializers/platform.py:63 msgid "Gather facts method" msgstr "收集信息方式" -#: assets/models/platform.py:36 assets/serializers/platform.py:66 +#: assets/models/platform.py:37 assets/serializers/platform.py:66 msgid "Change secret enabled" msgstr "启用改密" -#: assets/models/platform.py:38 assets/serializers/platform.py:67 +#: assets/models/platform.py:39 assets/serializers/platform.py:67 msgid "Change secret method" msgstr "改密方式" -#: assets/models/platform.py:40 assets/serializers/platform.py:68 +#: assets/models/platform.py:41 assets/serializers/platform.py:68 msgid "Push account enabled" msgstr "启用账号推送" -#: assets/models/platform.py:42 assets/serializers/platform.py:69 +#: assets/models/platform.py:43 assets/serializers/platform.py:69 msgid "Push account method" msgstr "账号推送方式" -#: assets/models/platform.py:44 assets/serializers/platform.py:64 +#: assets/models/platform.py:45 assets/serializers/platform.py:64 msgid "Verify account enabled" msgstr "开启账号验证" -#: assets/models/platform.py:46 assets/serializers/platform.py:65 +#: assets/models/platform.py:47 assets/serializers/platform.py:65 msgid "Verify account method" msgstr "账号验证方式" -#: assets/models/platform.py:67 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:68 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "元数据" -#: assets/models/platform.py:68 +#: assets/models/platform.py:69 msgid "Internal" msgstr "内置" -#: assets/models/platform.py:71 assets/serializers/platform.py:97 +#: assets/models/platform.py:73 assets/serializers/platform.py:109 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:73 assets/serializers/platform.py:124 +#: assets/models/platform.py:75 assets/serializers/platform.py:133 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:75 assets/serializers/platform.py:123 +#: assets/models/platform.py:77 assets/serializers/platform.py:132 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:76 assets/serializers/platform.py:106 +#: assets/models/platform.py:78 assets/serializers/platform.py:115 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:78 assets/serializers/platform.py:104 +#: assets/models/platform.py:81 assets/serializers/platform.py:113 msgid "Automation" msgstr "自动化" +#: assets/models/platform.py:83 assets/serializers/platform.py:118 +msgid "Custom fields" +msgstr "自定义属性" + #: assets/models/utils.py:18 #, python-format msgid "%(value)s is not an even number" @@ -1533,36 +1582,36 @@ msgid "" "type" msgstr "资产中批量更新平台,不符合平台类型跳过的资产" -#: assets/serializers/asset/common.py:123 +#: assets/serializers/asset/common.py:95 msgid "Auto fill" msgstr "自动代填" -#: assets/serializers/asset/common.py:134 assets/serializers/platform.py:102 -#: authentication/serializers/connect_token_secret.py:28 -#: authentication/serializers/connect_token_secret.py:66 +#: assets/serializers/asset/common.py:106 assets/serializers/platform.py:112 +#: authentication/serializers/connect_token_secret.py:29 +#: authentication/serializers/connect_token_secret.py:63 #: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99 msgid "Protocols" msgstr "协议组" -#: assets/serializers/asset/common.py:136 -#: assets/serializers/asset/common.py:157 +#: assets/serializers/asset/common.py:108 +#: assets/serializers/asset/common.py:130 msgid "Node path" msgstr "节点路径" -#: assets/serializers/asset/common.py:154 -#: assets/serializers/asset/common.py:308 +#: assets/serializers/asset/common.py:127 +#: assets/serializers/asset/common.py:299 msgid "Auto info" msgstr "自动化信息" -#: assets/serializers/asset/common.py:232 +#: assets/serializers/asset/common.py:220 msgid "Platform not exist" msgstr "平台不存在" -#: assets/serializers/asset/common.py:267 +#: assets/serializers/asset/common.py:255 msgid "port out of range (1-65535)" msgstr "端口超出范围 (1-65535)" -#: assets/serializers/asset/common.py:274 +#: assets/serializers/asset/common.py:262 msgid "Protocol is required: {}" msgstr "协议是必填的: {}" @@ -1611,7 +1660,7 @@ msgid "Disk total" msgstr "硬盘大小" #: assets/serializers/asset/host.py:21 -#: authentication/serializers/connect_token_secret.py:105 +#: authentication/serializers/connect_token_secret.py:103 msgid "OS" msgstr "操作系统" @@ -1623,6 +1672,10 @@ msgstr "系统版本" msgid "OS arch" msgstr "系统架构" +#: assets/serializers/asset/host.py:27 +msgid "Info" +msgstr "信息" + #: assets/serializers/cagegory.py:9 msgid "Constraints" msgstr "约束" @@ -1651,11 +1704,11 @@ msgstr "不能包含: /" msgid "The same level node name cannot be the same" msgstr "同级别节点名字不能重复" -#: assets/serializers/platform.py:25 +#: assets/serializers/platform.py:26 msgid "SFTP enabled" msgstr "SFTP 已启用" -#: assets/serializers/platform.py:26 +#: assets/serializers/platform.py:27 msgid "SFTP home" msgstr "SFTP 根路径" @@ -1671,15 +1724,17 @@ msgstr "启用账号收集" msgid "Gather accounts method" msgstr "收集账号方式" -#: assets/serializers/platform.py:125 +#: assets/serializers/platform.py:134 msgid "Default Domain" msgstr "默认网域" -#: assets/serializers/platform.py:137 -#, fuzzy -#| msgid "Protocol is required: {}" +#: assets/serializers/platform.py:143 +msgid "type is required" +msgstr "类型 该字段是必填项。" + +#: assets/serializers/platform.py:173 msgid "Protocols is required" -msgstr "协议是必填的: {}" +msgstr "协议是必填的" #: assets/signal_handlers/asset.py:26 assets/tasks/ping.py:35 msgid "Test assets connectivity " @@ -1814,7 +1869,7 @@ msgid "Change password" msgstr "改密" #: audits/const.py:34 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:161 +#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:163 #: terminal/serializers/session.py:48 msgid "Terminal" msgstr "终端" @@ -2477,23 +2532,23 @@ msgstr "异地登录提醒" msgid "binding reminder" msgstr "绑定提醒" -#: authentication/serializers/connect_token_secret.py:106 +#: authentication/serializers/connect_token_secret.py:104 msgid "Is builtin" msgstr "内置的" -#: authentication/serializers/connect_token_secret.py:110 +#: authentication/serializers/connect_token_secret.py:108 msgid "Options" msgstr "选项" -#: authentication/serializers/connect_token_secret.py:117 +#: authentication/serializers/connect_token_secret.py:115 msgid "Component" msgstr "组件" -#: authentication/serializers/connect_token_secret.py:128 +#: authentication/serializers/connect_token_secret.py:126 msgid "Expired now" msgstr "立刻过期" -#: authentication/serializers/connect_token_secret.py:148 +#: authentication/serializers/connect_token_secret.py:145 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" @@ -2997,10 +3052,8 @@ msgid "Parse file error: {}" msgstr "解析文件错误: {}" #: common/drf/parsers/excel.py:14 -#, fuzzy -#| msgid "Invalid zip file" msgid "Invalid excel file" -msgstr "无效的 zip 文件" +msgstr "无效的 excel 文件" #: common/exceptions.py:15 #, python-format @@ -3383,6 +3436,10 @@ msgstr "参数" msgid "Creator" msgstr "创建者" +#: ops/models/base.py:19 +msgid "Account policy" +msgstr "账号策略" + #: ops/models/base.py:20 msgid "Last execution" msgstr "最后执行" @@ -3416,13 +3473,6 @@ msgstr "可以查看任务监控" msgid "Kwargs" msgstr "其它参数" -#: ops/models/celery.py:60 tickets/models/comment.py:13 -#: tickets/models/ticket/general.py:45 tickets/models/ticket/general.py:279 -#: tickets/serializers/super_ticket.py:14 -#: tickets/serializers/ticket/ticket.py:21 -msgid "State" -msgstr "状态" - #: ops/models/celery.py:61 terminal/models/session/sharing.py:114 #: tickets/const.py:25 msgid "Finished" @@ -3934,7 +3984,7 @@ msgid "My assets" msgstr "我的资产" #: rbac/tree.py:56 terminal/models/applet/applet.py:43 -#: terminal/models/applet/applet.py:154 terminal/models/applet/host.py:28 +#: terminal/models/applet/applet.py:156 terminal/models/applet/host.py:28 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" @@ -4432,7 +4482,7 @@ msgid "SSO auth key TTL" msgstr "令牌有效期" #: settings/serializers/auth/sso.py:17 -#: xpack/plugins/cloud/serializers/account_attrs.py:194 +#: xpack/plugins/cloud/serializers/account_attrs.py:184 msgid "Unit: second" msgstr "单位: 秒" @@ -4999,11 +5049,11 @@ msgid "" "Tip: Default resolution to use when connecting graphical assets in Luna pages" msgstr "提示:在Luna 页面中连接图形化资产时默认使用的分辨率" -#: settings/tasks/ldap.py:24 +#: settings/tasks/ldap.py:25 msgid "Import ldap user" msgstr "导入 LDAP 用户" -#: settings/tasks/ldap.py:45 +#: settings/tasks/ldap.py:47 msgid "Periodic import ldap user" msgstr "周期导入 LDAP 用户" @@ -5189,13 +5239,13 @@ msgstr "过期。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" -" 您的密码已经过期,请点击 链接 更新密码\n" +" 您的密码已经过期,请点击 链接 更新密码\n" " " #: templates/_message.html:30 @@ -5219,8 +5269,8 @@ msgstr "" #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" @@ -5232,13 +5282,13 @@ msgstr "" #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" +" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -5449,7 +5499,7 @@ msgstr "主机" msgid "Applet pkg not valid, Missing file {}" msgstr "Applet pkg 无效,缺少文件 {}" -#: terminal/models/applet/applet.py:156 terminal/models/applet/host.py:34 +#: terminal/models/applet/applet.py:158 terminal/models/applet/host.py:34 #: terminal/models/applet/host.py:106 msgid "Hosting" msgstr "宿主机" @@ -5555,28 +5605,28 @@ msgid "Default storage" msgstr "默认存储" #: terminal/models/component/storage.py:140 -#: terminal/models/component/terminal.py:90 +#: terminal/models/component/terminal.py:91 msgid "Command storage" msgstr "命令存储" #: terminal/models/component/storage.py:200 -#: terminal/models/component/terminal.py:91 +#: terminal/models/component/terminal.py:92 msgid "Replay storage" msgstr "录像存储" -#: terminal/models/component/terminal.py:87 +#: terminal/models/component/terminal.py:88 msgid "type" msgstr "类型" -#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:51 +#: terminal/models/component/terminal.py:90 terminal/serializers/command.py:51 msgid "Remote Address" msgstr "远端地址" -#: terminal/models/component/terminal.py:92 +#: terminal/models/component/terminal.py:93 msgid "Application User" msgstr "应用用户" -#: terminal/models/component/terminal.py:163 +#: terminal/models/component/terminal.py:165 msgid "Can view terminal config" msgstr "可以查看终端配置" @@ -6541,10 +6591,8 @@ msgid "Avatar url" msgstr "头像路径" #: users/serializers/user.py:171 -#, fuzzy -#| msgid "One level" msgid "MFA level" -msgstr "1 级" +msgstr "MFA 级别" #: users/serializers/user.py:277 msgid "Select users" @@ -7261,13 +7309,13 @@ msgstr "JSON 格式的文件" msgid "IP address invalid `{}`, {}" msgstr "IP 地址无效: `{}`, {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:162 msgid "" "Format for comma-delimited string,Such as: 192.168.1.0/24, " "10.0.0.0-10.0.0.255" msgstr "格式为逗号分隔的字符串,如:192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:176 +#: xpack/plugins/cloud/serializers/account_attrs.py:166 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -7276,19 +7324,19 @@ msgstr "" "端口用来检测 IP 地址的有效性,在同步任务执行时,只会同步有效的 IP 地址。
" "如果端口为 0,则表示所有 IP 地址均有效。" -#: xpack/plugins/cloud/serializers/account_attrs.py:184 +#: xpack/plugins/cloud/serializers/account_attrs.py:174 msgid "Hostname prefix" msgstr "主机名前缀" -#: xpack/plugins/cloud/serializers/account_attrs.py:187 +#: xpack/plugins/cloud/serializers/account_attrs.py:177 msgid "IP segment" msgstr "IP 网段" -#: xpack/plugins/cloud/serializers/account_attrs.py:191 +#: xpack/plugins/cloud/serializers/account_attrs.py:181 msgid "Test port" msgstr "测试端口" -#: xpack/plugins/cloud/serializers/account_attrs.py:194 +#: xpack/plugins/cloud/serializers/account_attrs.py:184 msgid "Test timeout" msgstr "测试超时时间" @@ -7368,26 +7416,31 @@ msgstr "许可证导入成功" msgid "License is invalid" msgstr "无效的许可证" -#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:138 +#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:135 msgid "License" msgstr "许可证" -#: xpack/plugins/license/models.py:80 +#: xpack/plugins/license/models.py:79 msgid "Standard edition" msgstr "标准版" -#: xpack/plugins/license/models.py:82 +#: xpack/plugins/license/models.py:81 msgid "Enterprise edition" msgstr "企业版" -#: xpack/plugins/license/models.py:84 +#: xpack/plugins/license/models.py:83 msgid "Ultimate edition" msgstr "旗舰版" -#: xpack/plugins/license/models.py:86 +#: xpack/plugins/license/models.py:85 msgid "Community edition" msgstr "社区版" +#, fuzzy +#~| msgid "Custom user" +#~ msgid "Custom" +#~ msgstr "自定义用户" + #~ msgid "API Server" #~ msgstr "API 服务" diff --git a/apps/terminal/api/applet/applet.py b/apps/terminal/api/applet/applet.py index 10fb55091..116aec94e 100644 --- a/apps/terminal/api/applet/applet.py +++ b/apps/terminal/api/applet/applet.py @@ -64,13 +64,7 @@ class DownloadUploadMixin: if instance and not update: return Response({'error': 'Applet already exists: {}'.format(name)}, status=400) - serializer = serializers.AppletSerializer(data=manifest, instance=instance) - serializer.is_valid(raise_exception=True) - save_to = default_storage.path('applets/{}'.format(name)) - if os.path.exists(save_to): - shutil.rmtree(save_to) - shutil.move(tmp_dir, save_to) - serializer.save() + applet, serializer = Applet.install_from_dir(tmp_dir) return Response(serializer.data, status=201) @action(detail=True, methods=['get']) diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index bb6e11179..c6e938028 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -12,7 +12,6 @@ from rest_framework.serializers import ValidationError from common.db.models import JMSBaseModel from common.utils import lazyproperty, get_logger -from jumpserver.utils import has_valid_xpack_license logger = get_logger(__name__) @@ -91,24 +90,48 @@ class Applet(JMSBaseModel): return manifest @classmethod - def install_from_dir(cls, path): + def load_platform_if_need(cls, d): + from assets.serializers import PlatformSerializer + + if not os.path.exists(os.path.join(d, 'platform.yml')): + return + try: + with open(os.path.join(d, 'platform.yml')) as f: + data = yaml.safe_load(f) + except Exception as e: + raise ValidationError({'error': _('Load platform.yml failed: {}').format(e)}) + + if data['category'] != 'custom': + raise ValidationError({'error': _('Only support custom platform')}) + + try: + tp = data['type'] + except KeyError: + raise ValidationError({'error': _('Missing type in platform.yml')}) + + s = PlatformSerializer(data=data) + s.add_type_choices(tp, tp) + s.is_valid(raise_exception=True) + s.save() + + @classmethod + def install_from_dir(cls, path, builtin=True): from terminal.serializers import AppletSerializer manifest = cls.validate_pkg(path) name = manifest['name'] - if not has_valid_xpack_license() and name.lower() in ('navicat',): - return - instance = cls.objects.filter(name=name).first() serializer = AppletSerializer(instance=instance, data=manifest) serializer.is_valid() - serializer.save(builtin=True) - pkg_path = default_storage.path('applets/{}'.format(name)) + serializer.save(builtin=builtin) + cls.load_platform_if_need(path) + + pkg_path = default_storage.path('applets/{}'.format(name)) if os.path.exists(pkg_path): shutil.rmtree(pkg_path) shutil.copytree(path, pkg_path) - return instance + return instance, serializer def select_host_account(self): # 选择激活的发布机 diff --git a/apps/terminal/models/component/terminal.py b/apps/terminal/models/component/terminal.py index 88c2c6154..74364e06a 100644 --- a/apps/terminal/models/component/terminal.py +++ b/apps/terminal/models/component/terminal.py @@ -1,3 +1,5 @@ +import time +import uuid from django.conf import settings from django.core.cache import cache from django.db import models @@ -139,6 +141,7 @@ class Terminal(StorageMixin, TerminalStatusMixin, JMSBaseModel): if self.user: setattr(self.user, SKIP_SIGNAL, True) self.user.delete() + self.name = self.name + '_' + uuid.uuid4().hex[:8] self.user = None self.is_deleted = True self.save()