merge: with dev

pull/9680/head
ibuler 2023-02-22 11:27:53 +08:00
commit 0996b1fbf5
28 changed files with 198 additions and 100 deletions

View File

@ -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)

View File

@ -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', {})

View File

@ -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

View File

@ -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 }}"

View File

@ -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 }}"

View File

@ -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 }}'

View File

@ -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

View File

@ -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'))

View File

@ -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']

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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
},
}

View File

@ -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)

View File

@ -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")

View File

@ -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):

View File

@ -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)

View File

@ -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:

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:014483808a830a01f5432fdc44bc34f7f392e53a160ffa97eb377dbb49e0ec9a
size 135547
oid sha256:af57d16430705feb02ebbb99fc3a2f5fc3bab69209f558aa4d69b1e8055a6f5f
size 136036

View File

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 "同期インスタンス タスクの実行記録を定期的にクリアする"

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1c1524b6173a2613845d9450d84ef8ca9cf1be6d0f7cdae2a89f6131d6abc1f1
size 111449
oid sha256:3b6ee4a378810f2515be5020e3fa0b1297e1c207260ca60bb14dc5407ca19c43
size 111750

View File

@ -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 <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\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 "清理审计日志"

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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"))