diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index 631669acc..17938358c 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -45,6 +45,7 @@ class AccountViewSet(OrgBulkModelViewSet): accounts = asset.accounts.all() else: accounts = [] + accounts = self.filter_queryset(accounts) serializer = serializers.AccountSerializer(accounts, many=True) return Response(data=serializer.data) diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py index 050e69052..e5d4b36bb 100644 --- a/apps/accounts/api/account/task.py +++ b/apps/accounts/api/account/task.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from accounts import serializers from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task +from assets.exceptions import NotSupportedTemporarilyError __all__ = [ 'AccountsTaskCreateAPI', @@ -28,6 +29,11 @@ class AccountsTaskCreateAPI(CreateAPIView): if data['action'] == 'push': task = push_accounts_to_assets_task.delay(account_ids) else: + account = accounts[0] + asset = account.asset + if not asset.auto_info['ansible_enabled'] or \ + not asset.auto_info['ping_enabled']: + raise NotSupportedTemporarilyError() task = verify_accounts_connectivity_task.delay(account_ids) data = getattr(serializer, '_data', {}) diff --git a/apps/accounts/automations/gather_accounts/manager.py b/apps/accounts/automations/gather_accounts/manager.py index 848423164..5be1f423b 100644 --- a/apps/accounts/automations/gather_accounts/manager.py +++ b/apps/accounts/automations/gather_accounts/manager.py @@ -1,7 +1,7 @@ -from orgs.utils import tmp_to_org -from common.utils import get_logger +from accounts.const import AutomationTypes from accounts.models import GatheredAccount -from accounts.const import AutomationTypes, Source +from common.utils import get_logger +from orgs.utils import tmp_to_org from .filter import GatherAccountsFilter from ..base.manager import AccountBasePlaybookManager diff --git a/apps/accounts/automations/verify_account/database/mongodb/main.yml b/apps/accounts/automations/verify_account/database/mongodb/main.yml index 261fe63ca..483bfc127 100644 --- a/apps/accounts/automations/verify_account/database/mongodb/main.yml +++ b/apps/accounts/automations/verify_account/database/mongodb/main.yml @@ -6,8 +6,8 @@ tasks: - name: Verify account mongodb_ping: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" login_database: "{{ jms_asset.spec_info.db_name }}" diff --git a/apps/accounts/automations/verify_account/database/oracle/main.yml b/apps/accounts/automations/verify_account/database/oracle/main.yml index 12896f09a..3da515e4f 100644 --- a/apps/accounts/automations/verify_account/database/oracle/main.yml +++ b/apps/accounts/automations/verify_account/database/oracle/main.yml @@ -6,9 +6,9 @@ tasks: - name: Verify account oracle_ping: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" login_database: "{{ jms_asset.spec_info.db_name }}" - mode: "{{ jms_account.mode }}" + mode: "{{ account.mode }}" diff --git a/apps/accounts/automations/verify_account/database/sqlserver/main.yml b/apps/accounts/automations/verify_account/database/sqlserver/main.yml index bb079fa59..fa6c78ed7 100644 --- a/apps/accounts/automations/verify_account/database/sqlserver/main.yml +++ b/apps/accounts/automations/verify_account/database/sqlserver/main.yml @@ -6,8 +6,8 @@ tasks: - name: Verify account community.general.mssql_script: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" name: '{{ jms_asset.spec_info.db_name }}' diff --git a/apps/accounts/models/base.py b/apps/accounts/models/base.py index cd1fef5e8..7233a12a2 100644 --- a/apps/accounts/models/base.py +++ b/apps/accounts/models/base.py @@ -109,7 +109,7 @@ class BaseAccount(JMSOrgBaseModel): @property def private_key_path(self): - if not self.secret_type != SecretType.SSH_KEY \ + if self.secret_type != SecretType.SSH_KEY \ or not self.secret \ or not self.private_key: return None diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py index 0939558e1..6b3792559 100644 --- a/apps/accounts/serializers/automations/base.py +++ b/apps/accounts/serializers/automations/base.py @@ -1,14 +1,14 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers -from ops.mixin import PeriodTaskSerializerMixin +from accounts.models import AutomationExecution from assets.const import AutomationTypes from assets.models import Asset, Node, BaseAutomation -from accounts.models import AutomationExecution -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from common.utils import get_logger from common.const.choices import Trigger from common.serializers.fields import ObjectRelatedField, LabeledChoiceField +from common.utils import get_logger +from ops.mixin import PeriodTaskSerializerMixin +from orgs.mixins.serializers import BulkOrgResourceModelSerializer logger = get_logger(__file__) @@ -37,6 +37,17 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe 'executed_amount': {'label': _('Executed amount')}, } + def validate_name(self, name): + if self.instance: + return name + if BaseAutomation.objects.filter(name=name, type=self.model_type).exists(): + raise serializers.ValidationError(_('Name already exists')) + return name + + @property + def model_type(self): + raise NotImplementedError + class AutomationExecutionSerializer(serializers.ModelSerializer): snapshot = serializers.SerializerMethodField(label=_('Automation snapshot')) diff --git a/apps/accounts/serializers/automations/change_secret.py b/apps/accounts/serializers/automations/change_secret.py index 6b7fd86d6..cd597a805 100644 --- a/apps/accounts/serializers/automations/change_secret.py +++ b/apps/accounts/serializers/automations/change_secret.py @@ -4,13 +4,13 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers from accounts.const import ( - DEFAULT_PASSWORD_RULES, SecretType, SecretStrategy, SSHKeyStrategy + AutomationTypes, DEFAULT_PASSWORD_RULES, + SecretType, SecretStrategy, SSHKeyStrategy ) from accounts.models import ( Account, ChangeSecretAutomation, - ChangeSecretRecord + ChangeSecretRecord, AutomationExecution ) -from accounts.models import AutomationExecution from accounts.serializers import AuthValidateMixin from assets.models import Asset from common.serializers.fields import LabeledChoiceField, ObjectRelatedField @@ -53,10 +53,14 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ 'ssh_key_change_strategy', 'passphrase', 'recipients', ] extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{ + 'accounts': {'required': True}, 'recipients': {'label': _('Recipient'), 'help_text': _( "Currently only mail sending is supported" )}, }} + @property + def model_type(self): + return AutomationTypes.change_secret def validate_password_rules(self, password_rules): secret_type = self.initial_data['secret_type'] diff --git a/apps/accounts/serializers/automations/gather_accounts.py b/apps/accounts/serializers/automations/gather_accounts.py index ffca89198..b906e7881 100644 --- a/apps/accounts/serializers/automations/gather_accounts.py +++ b/apps/accounts/serializers/automations/gather_accounts.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -from django.utils.translation import ugettext_lazy as _ +from accounts.const import AutomationTypes from accounts.models import GatherAccountsAutomation from common.utils import get_logger @@ -20,3 +20,7 @@ class GatherAccountAutomationSerializer(BaseAutomationSerializer): fields = BaseAutomationSerializer.Meta.fields + read_only_fields extra_kwargs = BaseAutomationSerializer.Meta.extra_kwargs + + @property + def model_type(self): + return AutomationTypes.gather_accounts diff --git a/apps/accounts/serializers/automations/push_account.py b/apps/accounts/serializers/automations/push_account.py index 07ebaa2d0..b9982300b 100644 --- a/apps/accounts/serializers/automations/push_account.py +++ b/apps/accounts/serializers/automations/push_account.py @@ -1,4 +1,4 @@ -import copy +from accounts.const import AutomationTypes from accounts.models import PushAccountAutomation from .change_secret import ( ChangeSecretAutomationSerializer, ChangeSecretUpdateAssetSerializer, @@ -14,6 +14,10 @@ class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer): if n not in ['recipients'] ] + @property + def model_type(self): + return AutomationTypes.push_account + class PushAccountUpdateAssetSerializer(ChangeSecretUpdateAssetSerializer): class Meta: diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index ba2b36031..5b67e9114 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -8,12 +8,10 @@ from rest_framework.response import Response from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task from assets import serializers +from assets.exceptions import NotSupportedTemporarilyError from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend from assets.models import Asset, Gateway -from assets.tasks import ( - test_assets_connectivity_manual, - update_assets_hardware_info_manual -) +from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin from common.drf.filters import BaseFilterSet from common.utils import get_logger, is_uuid @@ -154,6 +152,10 @@ class AssetsTaskMixin: if data["action"] == "refresh": 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']: + raise NotSupportedTemporarilyError() task = test_assets_connectivity_manual(assets) return task diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index 68ebc028f..7f765d9fd 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -173,6 +173,12 @@ class BasePlaybookManager: self.runtime_dir, callback=PlaybookCallback(), ) + + with open(inventory_path, 'r') as f: + inventory_data = json.load(f) + if not inventory_data['all'].get('hosts'): + continue + runners.append(runer) return runners @@ -236,7 +242,6 @@ class BasePlaybookManager: print(">>> 开始执行任务\n") else: print("### 没有需要执行的任务\n") - return self.execution.date_start = timezone.now() for i, runner in enumerate(runners, start=1): @@ -245,11 +250,12 @@ class BasePlaybookManager: self.before_runner_start(runner) try: cb = runner.run(**kwargs) - self.delete_sensitive_data(runner.inventory) self.on_runner_success(runner, cb) except Exception as e: self.on_runner_failed(runner, e) - print('\n') + finally: + self.delete_sensitive_data(runner.inventory) + print('\n') self.execution.status = 'success' self.execution.date_finished = timezone.now() self.execution.save() diff --git a/apps/assets/const/database.py b/apps/assets/const/database.py index 7bf1aaf44..86e86ed40 100644 --- a/apps/assets/const/database.py +++ b/apps/assets/const/database.py @@ -38,9 +38,21 @@ class DatabaseTypes(BaseType): }, cls.REDIS: { 'ansible_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'gather_accounts_enabled': False, + 'verify_account_enabled': False, + 'change_secret_enabled': False, + 'push_account_enabled': False, }, cls.CLICKHOUSE: { 'ansible_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'gather_accounts_enabled': False, + 'verify_account_enabled': False, + 'change_secret_enabled': False, + 'push_account_enabled': False, }, } return constrains diff --git a/apps/assets/const/host.py b/apps/assets/const/host.py index a2e99865e..e7abba3fc 100644 --- a/apps/assets/const/host.py +++ b/apps/assets/const/host.py @@ -63,7 +63,13 @@ class HostTypes(BaseType): }, }, cls.OTHER_HOST: { - 'ansible_enabled': False + 'ansible_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'gather_accounts_enabled': False, + 'verify_account_enabled': False, + 'change_secret_enabled': False, + 'push_account_enabled': False }, } diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 509c2f58e..3c0989517 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -253,17 +253,20 @@ class AllTypes(ChoicesMixin): return data @classmethod - def create_or_update_by_platform_data(cls, name, platform_data): - from assets.models import Platform, PlatformAutomation, PlatformProtocol + def create_or_update_by_platform_data(cls, name, platform_data, platform_cls=None): + # 不直接用 Platform 是因为可能在 migrations 中使用 + from assets.models import Platform + if platform_cls is None: + platform_cls = Platform automation_data = platform_data.pop('automation', {}) protocols_data = platform_data.pop('protocols', []) - platform, created = Platform.objects.update_or_create( + platform, created = platform_cls.objects.update_or_create( defaults=platform_data, name=name ) if not platform.automation: - automation = PlatformAutomation.objects.create() + automation = platform_cls.automation.field.related_model.objects.create() platform.automation = automation platform.save() else: @@ -275,10 +278,13 @@ class AllTypes(ChoicesMixin): platform.protocols.all().delete() for p in protocols_data: p.pop('primary', None) - PlatformProtocol.objects.create(**p, platform=platform) + platform.protocols.create(**p) @classmethod - def create_or_update_internal_platforms(cls): + def create_or_update_internal_platforms(cls, platform_cls=None): + if platform_cls is None: + platform_cls = cls + print("\n\tCreate internal platforms") for category, type_cls in cls.category_types(): print("\t## Category: {}".format(category.label)) @@ -311,7 +317,7 @@ class AllTypes(ChoicesMixin): 'automation': {**default_automation, **_automation}, 'protocols': protocols_data } - cls.create_or_update_by_platform_data(name, platform_data) + cls.create_or_update_by_platform_data(name, platform_data, platform_cls=platform_cls) @classmethod def update_user_create_platforms(cls, platform_cls): @@ -328,4 +334,4 @@ class AllTypes(ChoicesMixin): for platform in user_platforms: print("\t- Update platform: {}".format(platform.name)) platform_data = cls.get_type_default_platform(platform.category, platform.type) - cls.create_or_update_by_platform_data(platform.name, platform_data) + cls.create_or_update_by_platform_data(platform.name, platform_data, platform_cls=platform_cls) diff --git a/apps/assets/exceptions.py b/apps/assets/exceptions.py index 099e68f11..ad22b6339 100644 --- a/apps/assets/exceptions.py +++ b/apps/assets/exceptions.py @@ -1,6 +1,12 @@ +from django.utils.translation import ugettext_lazy as _ from rest_framework import status + from common.exceptions import JMSException class NodeIsBeingUpdatedByOthers(JMSException): status_code = status.HTTP_409_CONFLICT + + +class NotSupportedTemporarilyError(JMSException): + default_detail = _("This function is not supported temporarily") diff --git a/apps/assets/migrations/0097_auto_20220426_1558.py b/apps/assets/migrations/0097_auto_20220426_1558.py index 2a16841d5..59d9726e7 100644 --- a/apps/assets/migrations/0097_auto_20220426_1558.py +++ b/apps/assets/migrations/0097_auto_20220426_1558.py @@ -5,7 +5,8 @@ from assets.const import AllTypes def create_internal_platforms(apps, *args): - AllTypes.create_or_update_internal_platforms() + platform_cls = apps.get_model('assets', 'Platform') + AllTypes.create_or_update_internal_platforms(platform_cls) def update_user_platforms(apps, *args): diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 9c31ee695..c5a9ed02d 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -277,6 +277,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali @atomic def update(self, instance, validated_data): + if not validated_data.get('accounts'): + validated_data.pop('accounts', None) nodes_display = validated_data.pop('nodes_display', '') instance = super().update(instance, validated_data) self.perform_nodes_display_create(instance, nodes_display) diff --git a/apps/audits/api.py b/apps/audits/api.py index e1aff002a..968fe19db 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -10,7 +10,7 @@ from rest_framework.permissions import IsAuthenticated from common.drf.filters import DatetimeRangeFilter from common.plugins.es import QuerySet as ESQuerySet from common.utils import is_uuid -from orgs.mixins.api import OrgReadonlyModelViewSet +from orgs.mixins.api import OrgReadonlyModelViewSet, OrgModelViewSet from orgs.utils import current_org, tmp_to_root_org from users.models import User from .backends import TYPE_ENGINE_MAPPING @@ -35,7 +35,7 @@ class JobAuditViewSet(OrgReadonlyModelViewSet): ordering = ['-date_start'] -class FTPLogViewSet(OrgReadonlyModelViewSet): +class FTPLogViewSet(OrgModelViewSet): model = FTPLog serializer_class = FTPLogSerializer extra_filter_backends = [DatetimeRangeFilter] @@ -45,6 +45,7 @@ class FTPLogViewSet(OrgReadonlyModelViewSet): filterset_fields = ['user', 'asset', 'account', 'filename'] search_fields = filterset_fields ordering = ['-date_start'] + http_method_names = ['post', 'get', 'head', 'options'] class UserLoginCommonMixin: diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 9380816b8..781aa1a90 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:014483808a830a01f5432fdc44bc34f7f392e53a160ffa97eb377dbb49e0ec9a -size 135547 +oid sha256:af57d16430705feb02ebbb99fc3a2f5fc3bab69209f558aa4d69b1e8055a6f5f +size 136036 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 038515a76..6e38486bd 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-02-21 13:46+0800\n" +"POT-Creation-Date: 2023-02-21 22:44+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -162,8 +162,8 @@ msgstr "作成のみ" #: accounts/models/automations/gather_account.py:16 #: accounts/serializers/account/account.py:95 #: accounts/serializers/account/gathered_account.py:10 -#: accounts/serializers/automations/change_secret.py:107 -#: accounts/serializers/automations/change_secret.py:127 +#: 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:279 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 @@ -192,8 +192,8 @@ msgid "Source" msgstr "ソース" #: accounts/models/account.py:58 -#: accounts/serializers/automations/change_secret.py:108 -#: accounts/serializers/automations/change_secret.py:128 +#: 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:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 @@ -238,7 +238,7 @@ msgstr "アセット アカウント テンプレートのパスワードを変 #: accounts/models/automations/backup_account.py:27 #: accounts/models/automations/change_secret.py:47 #: accounts/serializers/account/backup.py:34 -#: accounts/serializers/automations/change_secret.py:56 +#: accounts/serializers/automations/change_secret.py:57 msgid "Recipient" msgstr "受信者" @@ -269,7 +269,7 @@ msgstr "アカウントのバックアップスナップショット" #: accounts/models/automations/backup_account.py:94 #: accounts/serializers/account/backup.py:42 -#: accounts/serializers/automations/base.py:44 +#: accounts/serializers/automations/base.py:53 #: assets/models/automations/base.py:121 #: assets/serializers/automations/base.py:40 msgid "Trigger mode" @@ -281,8 +281,8 @@ msgid "Reason" msgstr "理由" #: accounts/models/automations/backup_account.py:99 -#: accounts/serializers/automations/change_secret.py:106 -#: accounts/serializers/automations/change_secret.py:129 +#: accounts/serializers/automations/change_secret.py:110 +#: accounts/serializers/automations/change_secret.py:133 #: ops/serializers/job.py:64 terminal/serializers/session.py:45 msgid "Is success" msgstr "成功は" @@ -540,7 +540,7 @@ msgid "Category" msgstr "カテゴリ" #: accounts/serializers/account/account.py:76 -#: accounts/serializers/automations/base.py:43 acls/models/command_acl.py:24 +#: accounts/serializers/automations/base.py:52 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:78 @@ -575,7 +575,7 @@ msgid "Executed amount" msgstr "実行回数" #: accounts/serializers/account/backup.py:35 -#: accounts/serializers/automations/change_secret.py:57 +#: accounts/serializers/automations/change_secret.py:58 msgid "Currently only mail sending is supported" msgstr "現在、メール送信のみがサポートされています" @@ -612,6 +612,10 @@ msgid "Nodes" msgstr "ノード" #: accounts/serializers/automations/base.py:42 +msgid "Name already exists" +msgstr "名前は既に存在します。" + +#: accounts/serializers/automations/base.py:51 #: assets/models/automations/base.py:117 #: assets/serializers/automations/base.py:39 msgid "Automation snapshot" @@ -621,20 +625,20 @@ msgstr "自動スナップショット" msgid "SSH Key strategy" msgstr "SSHキー戦略" -#: accounts/serializers/automations/change_secret.py:76 +#: accounts/serializers/automations/change_secret.py:80 msgid "* Please enter the correct password length" msgstr "* 正しいパスワードの長さを入力してください" -#: accounts/serializers/automations/change_secret.py:80 +#: accounts/serializers/automations/change_secret.py:84 msgid "* Password length range 6-30 bits" msgstr "* パスワードの長さの範囲6-30ビット" -#: accounts/serializers/automations/change_secret.py:110 +#: accounts/serializers/automations/change_secret.py:114 #: assets/models/automations/base.py:126 msgid "Automation task execution" msgstr "自動タスク実行履歴" -#: accounts/serializers/automations/change_secret.py:150 audits/const.py:52 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 #: terminal/const.py:59 terminal/models/session/sharing.py:103 @@ -642,7 +646,7 @@ msgstr "自動タスク実行履歴" msgid "Success" msgstr "成功" -#: accounts/serializers/automations/change_secret.py:151 audits/const.py:53 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:53 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:58 terminal/const.py:60 xpack/plugins/cloud/const.py:41 msgid "Failed" @@ -904,7 +908,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:144 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -1047,6 +1051,10 @@ msgstr "基本" msgid "Script" msgstr "脚本" +#: assets/exceptions.py:12 +msgid "This function is not supported temporarily" +msgstr "この機能は一時的にサポートされていません" + #: assets/models/_user.py:25 msgid "SSH private key" msgstr "SSH秘密鍵" @@ -7286,6 +7294,14 @@ msgstr "実行回数" msgid "Instance count" msgstr "インスタンス数" +#: xpack/plugins/cloud/tasks.py:27 +msgid "Run sync instance task" +msgstr "同期インスタンス タスクを実行する" + +#: xpack/plugins/cloud/tasks.py:41 +msgid "Period clean sync instance task execution" +msgstr "同期インスタンス タスクの実行記録を定期的にクリアする" + #: xpack/plugins/cloud/utils.py:69 msgid "Account unavailable" msgstr "利用できないアカウント" @@ -7365,9 +7381,3 @@ msgstr "コミュニティ版" #~ msgid "Remove asset from node" #~ msgstr "ノードからアセットを削除" - -#~ msgid "Run sync instance task" -#~ msgstr "同期インスタンス タスクを実行する" - -#~ msgid "Period clean sync instance task execution" -#~ msgstr "同期インスタンス タスクの実行記録を定期的にクリアする" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 70960ba2c..02f116670 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:1c1524b6173a2613845d9450d84ef8ca9cf1be6d0f7cdae2a89f6131d6abc1f1 -size 111449 +oid sha256:3b6ee4a378810f2515be5020e3fa0b1297e1c207260ca60bb14dc5407ca19c43 +size 111750 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index d884a2053..c3f661344 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-02-21 13:46+0800\n" +"POT-Creation-Date: 2023-02-21 22:44+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -161,8 +161,8 @@ msgstr "仅创建" #: accounts/models/automations/gather_account.py:16 #: accounts/serializers/account/account.py:95 #: accounts/serializers/account/gathered_account.py:10 -#: accounts/serializers/automations/change_secret.py:107 -#: accounts/serializers/automations/change_secret.py:127 +#: 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:279 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 @@ -191,8 +191,8 @@ msgid "Source" msgstr "来源" #: accounts/models/account.py:58 -#: accounts/serializers/automations/change_secret.py:108 -#: accounts/serializers/automations/change_secret.py:128 +#: 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:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 @@ -237,7 +237,7 @@ msgstr "可以更改资产账号模版密码" #: accounts/models/automations/backup_account.py:27 #: accounts/models/automations/change_secret.py:47 #: accounts/serializers/account/backup.py:34 -#: accounts/serializers/automations/change_secret.py:56 +#: accounts/serializers/automations/change_secret.py:57 msgid "Recipient" msgstr "收件人" @@ -268,7 +268,7 @@ msgstr "账号备份快照" #: accounts/models/automations/backup_account.py:94 #: accounts/serializers/account/backup.py:42 -#: accounts/serializers/automations/base.py:44 +#: accounts/serializers/automations/base.py:53 #: assets/models/automations/base.py:121 #: assets/serializers/automations/base.py:40 msgid "Trigger mode" @@ -280,8 +280,8 @@ msgid "Reason" msgstr "原因" #: accounts/models/automations/backup_account.py:99 -#: accounts/serializers/automations/change_secret.py:106 -#: accounts/serializers/automations/change_secret.py:129 +#: accounts/serializers/automations/change_secret.py:110 +#: accounts/serializers/automations/change_secret.py:133 #: ops/serializers/job.py:64 terminal/serializers/session.py:45 msgid "Is success" msgstr "是否成功" @@ -536,7 +536,7 @@ msgid "Category" msgstr "类别" #: accounts/serializers/account/account.py:76 -#: accounts/serializers/automations/base.py:43 acls/models/command_acl.py:24 +#: accounts/serializers/automations/base.py:52 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:78 @@ -571,7 +571,7 @@ msgid "Executed amount" msgstr "执行次数" #: accounts/serializers/account/backup.py:35 -#: accounts/serializers/automations/change_secret.py:57 +#: accounts/serializers/automations/change_secret.py:58 msgid "Currently only mail sending is supported" msgstr "当前只支持邮件发送" @@ -608,6 +608,10 @@ msgid "Nodes" msgstr "节点" #: accounts/serializers/automations/base.py:42 +msgid "Name already exists" +msgstr "名称已存在" + +#: accounts/serializers/automations/base.py:51 #: assets/models/automations/base.py:117 #: assets/serializers/automations/base.py:39 msgid "Automation snapshot" @@ -617,20 +621,20 @@ msgstr "自动化快照" msgid "SSH Key strategy" msgstr "SSH 密钥更改方式" -#: accounts/serializers/automations/change_secret.py:76 +#: accounts/serializers/automations/change_secret.py:80 msgid "* Please enter the correct password length" msgstr "* 请输入正确的密码长度" -#: accounts/serializers/automations/change_secret.py:80 +#: accounts/serializers/automations/change_secret.py:84 msgid "* Password length range 6-30 bits" msgstr "* 密码长度范围 6-30 位" -#: accounts/serializers/automations/change_secret.py:110 +#: accounts/serializers/automations/change_secret.py:114 #: assets/models/automations/base.py:126 msgid "Automation task execution" msgstr "自动化任务执行历史" -#: accounts/serializers/automations/change_secret.py:150 audits/const.py:52 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 #: terminal/const.py:59 terminal/models/session/sharing.py:103 @@ -638,7 +642,7 @@ msgstr "自动化任务执行历史" msgid "Success" msgstr "成功" -#: accounts/serializers/automations/change_secret.py:151 audits/const.py:53 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:53 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:58 terminal/const.py:60 xpack/plugins/cloud/const.py:41 msgid "Failed" @@ -898,7 +902,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:144 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1039,6 +1043,10 @@ msgstr "基本" msgid "Script" msgstr "脚本" +#: assets/exceptions.py:12 +msgid "This function is not supported temporarily" +msgstr "暂时不支持此功能" + #: assets/models/_user.py:25 msgid "SSH private key" msgstr "SSH密钥" @@ -5613,7 +5621,7 @@ msgstr "图标" #: terminal/serializers/applet_host.py:24 msgid "Per Session" -msgstr "每会话" +msgstr "每用户" #: terminal/serializers/applet_host.py:25 msgid "Per Device" @@ -7191,6 +7199,14 @@ msgstr "执行次数" msgid "Instance count" msgstr "实例个数" +#: xpack/plugins/cloud/tasks.py:27 +msgid "Run sync instance task" +msgstr "执行同步实例任务" + +#: xpack/plugins/cloud/tasks.py:41 +msgid "Period clean sync instance task execution" +msgstr "定期清除同步实例任务执行记录" + #: xpack/plugins/cloud/utils.py:69 msgid "Account unavailable" msgstr "账号无效" @@ -7271,12 +7287,6 @@ msgstr "社区版" #~ msgid "Remove asset from node" #~ msgstr "从节点移除资产" -#~ msgid "Run sync instance task" -#~ msgstr "执行同步实例任务" - -#~ msgid "Period clean sync instance task execution" -#~ msgstr "定期清除同步实例任务执行记录" - #~ msgid "Clean audits log" #~ msgstr "清理审计日志" diff --git a/apps/ops/api/playbook.py b/apps/ops/api/playbook.py index ea856df37..b3c1fe564 100644 --- a/apps/ops/api/playbook.py +++ b/apps/ops/api/playbook.py @@ -6,6 +6,7 @@ from django.conf import settings from django.shortcuts import get_object_or_404 from rest_framework import status +from common.exceptions import JMSException from orgs.mixins.api import OrgBulkModelViewSet from ..exception import PlaybookNoValidEntry from ..models import Playbook @@ -39,7 +40,11 @@ class PlaybookViewSet(OrgBulkModelViewSet): if 'multipart/form-data' in self.request.headers['Content-Type']: src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name) dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__()) - unzip_playbook(src_path, dest_path) + try: + unzip_playbook(src_path, dest_path) + except RuntimeError as e: + raise JMSException(code='invalid_playbook_file', detail={"msg": "Unzip failed"}) + if 'main.yml' not in os.listdir(dest_path): raise PlaybookNoValidEntry @@ -145,16 +150,17 @@ class PlaybookFileBrowserAPIView(APIView): return Response(status=status.HTTP_400_BAD_REQUEST) file_path = os.path.join(work_path, file_key) + # rename if new_name: new_file_path = os.path.join(os.path.dirname(file_path), new_name) if os.path.exists(new_file_path): return Response({'msg': '{} already exists'.format(new_name)}, status=status.HTTP_400_BAD_REQUEST) os.rename(file_path, new_file_path) - file_path = new_file_path - - if not is_directory and content: - with open(file_path, 'w') as f: - f.write(content) + # edit content + else: + if not is_directory: + with open(file_path, 'w') as f: + f.write(content) return Response({'msg': 'ok'}) def delete(self, request, **kwargs): diff --git a/apps/perms/const.py b/apps/perms/const.py index 690fb2742..21fe2d763 100644 --- a/apps/perms/const.py +++ b/apps/perms/const.py @@ -36,8 +36,7 @@ class ActionChoices(BitChoices): return cls.copy | cls.paste @classmethod - def contains(cls, total, action): - action_value = getattr(cls, action) + def contains(cls, total, action_value): return action_value & total == action_value @classmethod diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index c71fe95c2..ee83c8e52 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -111,7 +111,8 @@ class Applet(JMSBaseModel): return instance def select_host_account(self): - hosts = list(self.hosts.all()) + # 选择激活的发布机 + hosts = list(self.hosts.filter(is_active=True).all()) if not hosts: return None diff --git a/apps/terminal/serializers/applet_host.py b/apps/terminal/serializers/applet_host.py index 8ae42c7de..54ca99cad 100644 --- a/apps/terminal/serializers/applet_host.py +++ b/apps/terminal/serializers/applet_host.py @@ -32,7 +32,7 @@ class DeployOptionsSerializer(serializers.Serializer): CORE_HOST = serializers.CharField(default=settings.SITE_URL, label=_('API Server'), max_length=1024) RDS_Licensing = serializers.BooleanField(default=False, label=_("RDS Licensing")) RDS_LicenseServer = serializers.CharField(default='127.0.0.1', label=_('RDS License Server'), max_length=1024) - RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=4, label=_('RDS Licensing Mode')) + RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=2, label=_('RDS Licensing Mode')) RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, label=_("RDS Single Session Per User")) RDS_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time"))