diff --git a/apps/accounts/api/account/__init__.py b/apps/accounts/api/account/__init__.py index ea5ab0cd1..f6d8376a6 100644 --- a/apps/accounts/api/account/__init__.py +++ b/apps/accounts/api/account/__init__.py @@ -1,4 +1,3 @@ from .account import * -from .backup import * +from .task import * from .template import * -from .gathered_account import * diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index 3d4bcebd5..631669acc 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -1,12 +1,11 @@ from django.shortcuts import get_object_or_404 from rest_framework.decorators import action -from rest_framework.generics import CreateAPIView, ListAPIView +from rest_framework.generics import ListAPIView from rest_framework.response import Response from accounts import serializers from accounts.filters import AccountFilterSet from accounts.models import Account -from accounts.tasks import verify_accounts_connectivity from assets.models import Asset from authentication.const import ConfirmType from common.permissions import UserConfirmation @@ -15,7 +14,7 @@ from orgs.mixins.api import OrgBulkModelViewSet __all__ = [ 'AccountViewSet', 'AccountSecretsViewSet', - 'AccountTaskCreateAPI', 'AccountHistoriesSecretAPI' + 'AccountHistoriesSecretAPI' ] from rbac.permissions import RBACPermission @@ -29,7 +28,6 @@ class AccountViewSet(OrgBulkModelViewSet): 'default': serializers.AccountSerializer, } rbac_perms = { - 'verify_account': 'accounts.test_account', 'partial_update': ['accounts.change_account'], 'su_from_accounts': 'accounts.view_account', } @@ -38,6 +36,7 @@ class AccountViewSet(OrgBulkModelViewSet): def su_from_accounts(self, request, *args, **kwargs): account_id = request.query_params.get('account') asset_id = request.query_params.get('asset') + if account_id: account = get_object_or_404(Account, pk=account_id) accounts = account.get_su_from_accounts() @@ -49,14 +48,6 @@ class AccountViewSet(OrgBulkModelViewSet): serializer = serializers.AccountSerializer(accounts, many=True) return Response(data=serializer.data) - @action(methods=['post'], detail=True, url_path='verify') - def verify_account(self, request, *args, **kwargs): - account = super().get_object() - account_ids = [account.id] - asset_ids = [account.asset_id] - task = verify_accounts_connectivity.delay(account_ids, asset_ids) - return Response(data={'task': task.id}) - class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet): """ @@ -84,33 +75,3 @@ class AccountHistoriesSecretAPI(RecordViewLogMixin, ListAPIView): def get_queryset(self): return self.model.objects.filter(id=self.kwargs.get('pk')) - - -class AccountTaskCreateAPI(CreateAPIView): - serializer_class = serializers.AccountTaskSerializer - search_fields = AccountViewSet.search_fields - filterset_class = AccountViewSet.filterset_class - - def check_permissions(self, request): - return request.user.has_perm('assets.test_assetconnectivity') - - def get_accounts(self): - queryset = Account.objects.all() - queryset = self.filter_queryset(queryset) - return queryset - - def perform_create(self, serializer): - accounts = self.get_accounts() - account_ids = accounts.values_list('id', flat=True) - asset_ids = [account.asset_id for account in accounts] - task = verify_accounts_connectivity.delay(account_ids, asset_ids) - data = getattr(serializer, '_data', {}) - data["task"] = task.id - setattr(serializer, '_data', data) - return task - - def get_exception_handler(self): - def handler(e, context): - return Response({"error": str(e)}, status=400) - - return handler diff --git a/apps/accounts/api/account/gathered_account.py b/apps/accounts/api/account/gathered_account.py deleted file mode 100644 index e47befac4..000000000 --- a/apps/accounts/api/account/gathered_account.py +++ /dev/null @@ -1,42 +0,0 @@ -from rest_framework import status -from rest_framework.decorators import action -from rest_framework.response import Response -from django.utils.translation import ugettext_lazy as _ - -from accounts import serializers -from accounts.const import Source -from accounts.models import GatheredAccount -from accounts.filters import GatheredAccountFilterSet -from orgs.mixins.api import OrgBulkModelViewSet - -__all__ = [ - 'GatheredAccountViewSet', -] - - -class GatheredAccountViewSet(OrgBulkModelViewSet): - model = GatheredAccount - search_fields = ('username',) - filterset_class = GatheredAccountFilterSet - serializer_classes = { - 'default': serializers.GatheredAccountSerializer, - } - rbac_perms = { - 'sync_account': 'assets.add_gatheredaccount', - } - - @action(methods=['post'], detail=True, url_path='sync') - def sync_account(self, request, *args, **kwargs): - gathered_account = super().get_object() - asset = gathered_account.asset - username = gathered_account.username - accounts = asset.accounts.filter(username=username) - if accounts.exists(): - accounts.update(source=Source.COLLECTED) - else: - asset.accounts.model.objects.create( - asset=asset, username=username, - name=f'{username}-{_("Collected")}', - source=Source.COLLECTED - ) - return Response(status=status.HTTP_201_CREATED) diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py new file mode 100644 index 000000000..050e69052 --- /dev/null +++ b/apps/accounts/api/account/task.py @@ -0,0 +1,42 @@ +from rest_framework.generics import CreateAPIView +from rest_framework.response import Response + +from accounts import serializers +from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task + +__all__ = [ + 'AccountsTaskCreateAPI', +] + + +class AccountsTaskCreateAPI(CreateAPIView): + serializer_class = serializers.AccountTaskSerializer + + def check_permissions(self, request): + act = request.data.get('action') + if act == 'push': + code = 'accounts.push_account' + else: + code = 'accounts.verify_account' + return request.user.has_perm(code) + + def perform_create(self, serializer): + data = serializer.validated_data + accounts = data.get('accounts', []) + account_ids = [a.id for a in accounts] + + if data['action'] == 'push': + task = push_accounts_to_assets_task.delay(account_ids) + else: + task = verify_accounts_connectivity_task.delay(account_ids) + + data = getattr(serializer, '_data', {}) + data["task"] = task.id + setattr(serializer, '_data', data) + return task + + def get_exception_handler(self): + def handler(e, context): + return Response({"error": str(e)}, status=400) + + return handler diff --git a/apps/accounts/api/automations/__init__.py b/apps/accounts/api/automations/__init__.py index 2b0aa0029..a03da88b0 100644 --- a/apps/accounts/api/automations/__init__.py +++ b/apps/accounts/api/automations/__init__.py @@ -1,3 +1,4 @@ +from .backup import * from .base import * from .change_secret import * from .gather_accounts import * diff --git a/apps/accounts/api/account/backup.py b/apps/accounts/api/automations/backup.py similarity index 91% rename from apps/accounts/api/account/backup.py rename to apps/accounts/api/automations/backup.py index fa068a0df..134f9548c 100644 --- a/apps/accounts/api/account/backup.py +++ b/apps/accounts/api/automations/backup.py @@ -7,7 +7,7 @@ from accounts import serializers from accounts.models import ( AccountBackupAutomation, AccountBackupExecution ) -from accounts.tasks import execute_account_backup_plan +from accounts.tasks import execute_account_backup_task from common.const.choices import Trigger from orgs.mixins.api import OrgBulkModelViewSet @@ -38,5 +38,5 @@ class AccountBackupPlanExecutionViewSet(viewsets.ModelViewSet): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) pid = serializer.data.get('plan') - task = execute_account_backup_plan.delay(pid=str(pid), trigger=Trigger.manual) + task = execute_account_backup_task.delay(pid=str(pid), trigger=Trigger.manual) return Response({'task': task.id}, status=status.HTTP_201_CREATED) diff --git a/apps/accounts/api/automations/base.py b/apps/accounts/api/automations/base.py index 700484e9b..7d5db0ac8 100644 --- a/apps/accounts/api/automations/base.py +++ b/apps/accounts/api/automations/base.py @@ -4,7 +4,7 @@ from rest_framework import status, mixins, viewsets from rest_framework.response import Response from accounts.models import AutomationExecution -from accounts.tasks import execute_automation +from accounts.tasks import execute_account_automation_task from assets import serializers from assets.models import BaseAutomation from common.const.choices import Trigger @@ -109,7 +109,7 @@ class AutomationExecutionViewSet( serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) automation = serializer.validated_data.get('automation') - task = execute_automation.delay( + task = execute_account_automation_task.delay( pid=str(automation.pk), trigger=Trigger.manual, tp=self.tp ) return Response({'task': task.id}, status=status.HTTP_201_CREATED) diff --git a/apps/accounts/api/automations/gather_accounts.py b/apps/accounts/api/automations/gather_accounts.py index 59f55c60e..3abca94dd 100644 --- a/apps/accounts/api/automations/gather_accounts.py +++ b/apps/accounts/api/automations/gather_accounts.py @@ -1,13 +1,22 @@ # -*- coding: utf-8 -*- # +from django.utils.translation import ugettext_lazy as _ +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + from accounts import serializers from accounts.const import AutomationTypes +from accounts.const import Source +from accounts.filters import GatheredAccountFilterSet from accounts.models import GatherAccountsAutomation +from accounts.models import GatheredAccount from orgs.mixins.api import OrgBulkModelViewSet from .base import AutomationExecutionViewSet __all__ = [ - 'GatherAccountsAutomationViewSet', 'GatherAccountsExecutionViewSet' + 'GatherAccountsAutomationViewSet', 'GatherAccountsExecutionViewSet', + 'GatheredAccountViewSet' ] @@ -31,3 +40,32 @@ class GatherAccountsExecutionViewSet(AutomationExecutionViewSet): queryset = super().get_queryset() queryset = queryset.filter(automation__type=self.tp) return queryset + + +class GatheredAccountViewSet(OrgBulkModelViewSet): + model = GatheredAccount + search_fields = ('username',) + filterset_class = GatheredAccountFilterSet + serializer_classes = { + 'default': serializers.GatheredAccountSerializer, + } + rbac_perms = { + 'sync_account': 'assets.add_gatheredaccount', + } + + @action(methods=['post'], detail=True, url_path='sync') + def sync_account(self, request, *args, **kwargs): + gathered_account = super().get_object() + asset = gathered_account.asset + username = gathered_account.username + accounts = asset.accounts.filter(username=username) + + if accounts.exists(): + accounts.update(source=Source.COLLECTED) + else: + asset.accounts.model.objects.create( + asset=asset, username=username, + name=f'{username}-{_("Collected")}', + source=Source.COLLECTED + ) + return Response(status=status.HTTP_201_CREATED) diff --git a/apps/accounts/automations/base/manager.py b/apps/accounts/automations/base/manager.py index 4e06217b3..6251c93a2 100644 --- a/apps/accounts/automations/base/manager.py +++ b/apps/accounts/automations/base/manager.py @@ -17,19 +17,19 @@ class VerifyHostCallbackMixin: def host_callback(self, host, asset=None, account=None, automation=None, path_dir=None, **kwargs): host = super().host_callback( - host, asset=asset, account=account, automation=automation, - path_dir=path_dir, **kwargs + host, asset=asset, account=account, + automation=automation, path_dir=path_dir, **kwargs ) if host.get('error'): return host accounts = asset.accounts.all() accounts = self.get_accounts(account, accounts) - inventory_hosts = [] + for account in accounts: h = deepcopy(host) - h['name'] += '_' + account.username + h['name'] += '(' + account.username + ')' self.host_account_mapper[h['name']] = account secret = account.secret diff --git a/apps/accounts/automations/change_secret/host/posix/main.yml b/apps/accounts/automations/change_secret/host/posix/main.yml index 1a4e6a6a4..b4e6aede6 100644 --- a/apps/accounts/automations/change_secret/host/posix/main.yml +++ b/apps/accounts/automations/change_secret/host/posix/main.yml @@ -3,10 +3,6 @@ tasks: - name: Test privileged account ansible.builtin.ping: - # - # - name: print variables - # debug: - # msg: "Username: {{ account.username }}, Secret: {{ account.secret }}, Secret type: {{ secret_type }}" - name: Change password ansible.builtin.user: @@ -26,8 +22,8 @@ regexp: "{{ kwargs.regexp }}" state: absent when: - - secret_type == "ssh_key" - - kwargs.strategy == "set_jms" + - secret_type == "ssh_key" + - kwargs.strategy == "set_jms" - name: Change SSH key ansible.builtin.authorized_key: diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py index 07e10e83c..c076ab56b 100644 --- a/apps/accounts/automations/change_secret/manager.py +++ b/apps/accounts/automations/change_secret/manager.py @@ -99,7 +99,7 @@ class ChangeSecretManager(AccountBasePlaybookManager): for account in accounts: h = deepcopy(host) - h['name'] += '_' + account.username + h['name'] += '(' + account.username + ')' new_secret = self.get_secret() recorder = ChangeSecretRecord( 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/push_account/manager.py b/apps/accounts/automations/push_account/manager.py index eb8f1d4ac..f2f21c51a 100644 --- a/apps/accounts/automations/push_account/manager.py +++ b/apps/accounts/automations/push_account/manager.py @@ -68,7 +68,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager): for account in accounts: h = deepcopy(host) - h['name'] += '_' + account.username + h['name'] += '(' + account.username + ')' new_secret = self.get_secret() self.name_recorder_mapper[h['name']] = { diff --git a/apps/accounts/migrations/0001_initial.py b/apps/accounts/migrations/0001_initial.py index 6e211a7fc..b8fe35670 100644 --- a/apps/accounts/migrations/0001_initial.py +++ b/apps/accounts/migrations/0001_initial.py @@ -50,7 +50,6 @@ class Migration(migrations.Migration): options={ 'verbose_name': 'Account', 'permissions': [('view_accountsecret', 'Can view asset account secret'), - ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')], 'unique_together': {('username', 'asset', 'secret_type'), ('name', 'asset')}, diff --git a/apps/accounts/migrations/0007_alter_account_options.py b/apps/accounts/migrations/0007_alter_account_options.py index 73193e9a4..4ec798a21 100644 --- a/apps/accounts/migrations/0007_alter_account_options.py +++ b/apps/accounts/migrations/0007_alter_account_options.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='account', - options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')], 'verbose_name': 'Account'}, + options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'}, ), ] diff --git a/apps/accounts/migrations/0008_alter_account_options.py b/apps/accounts/migrations/0008_alter_account_options.py new file mode 100644 index 000000000..949840740 --- /dev/null +++ b/apps/accounts/migrations/0008_alter_account_options.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.14 on 2023-02-21 05:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0007_alter_account_options'), + ] + + operations = [ + migrations.AlterModelOptions( + name='account', + options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'}, + ), + ] diff --git a/apps/accounts/models/__init__.py b/apps/accounts/models/__init__.py index 46de8344e..c40ee786d 100644 --- a/apps/accounts/models/__init__.py +++ b/apps/accounts/models/__init__.py @@ -1,4 +1,3 @@ from .base import * from .account import * from .automations import * -from .gathered_account import * diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index ed5fb72fa..7367c53de 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -64,6 +64,8 @@ class Account(AbsConnectivity, BaseAccount): ('view_accountsecret', _('Can view asset account secret')), ('view_historyaccount', _('Can view asset history account')), ('view_historyaccountsecret', _('Can view asset history account secret')), + ('verify_account', _('Can verify account')), + ('push_account', _('Can push account')), ] @lazyproperty diff --git a/apps/accounts/models/automations/backup_account.py b/apps/accounts/models/automations/backup_account.py index 3c213b00d..fba3b3149 100644 --- a/apps/accounts/models/automations/backup_account.py +++ b/apps/accounts/models/automations/backup_account.py @@ -7,11 +7,11 @@ from celery import current_task from django.db import models from django.db.models import F from django.utils.translation import ugettext_lazy as _ -from common.utils import lazyproperty from common.const.choices import Trigger from common.db.encoder import ModelJSONFieldEncoder from common.utils import get_logger +from common.utils import lazyproperty from ops.mixin import PeriodTaskModelMixin from orgs.mixins.models import OrgModelMixin, JMSOrgBaseModel @@ -36,9 +36,9 @@ class AccountBackupAutomation(PeriodTaskModelMixin, JMSOrgBaseModel): verbose_name = _('Account backup plan') def get_register_task(self): - from ...tasks import execute_account_backup_plan + from ...tasks import execute_account_backup_task name = "account_backup_plan_period_{}".format(str(self.id)[:8]) - task = execute_account_backup_plan.name + task = execute_account_backup_task.name args = (str(self.id), Trigger.timing) kwargs = {} return name, task, args, kwargs diff --git a/apps/accounts/models/automations/base.py b/apps/accounts/models/automations/base.py index 30c5df760..9477a7fdb 100644 --- a/apps/accounts/models/automations/base.py +++ b/apps/accounts/models/automations/base.py @@ -1,6 +1,6 @@ from django.utils.translation import gettext_lazy as _ -from accounts.tasks import execute_automation +from accounts.tasks import execute_account_automation_task from assets.models.automations import ( BaseAutomation as AssetBaseAutomation, AutomationExecution as AssetAutomationExecution @@ -16,7 +16,7 @@ class AccountBaseAutomation(AssetBaseAutomation): @property def execute_task(self): - return execute_automation + return execute_account_automation_task @property def execution_model(self): diff --git a/apps/accounts/models/automations/change_secret.py b/apps/accounts/models/automations/change_secret.py index d52fc2cae..76ae9b4f2 100644 --- a/apps/accounts/models/automations/change_secret.py +++ b/apps/accounts/models/automations/change_secret.py @@ -16,11 +16,11 @@ class ChangeSecretMixin(models.Model): choices=SecretType.choices, max_length=16, default=SecretType.PASSWORD, verbose_name=_('Secret type') ) + secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret')) secret_strategy = models.CharField( choices=SecretStrategy.choices, max_length=16, default=SecretStrategy.custom, verbose_name=_('Secret strategy') ) - secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret')) password_rules = models.JSONField(default=dict, verbose_name=_('Password rules')) ssh_key_change_strategy = models.CharField( choices=SSHKeyStrategy.choices, max_length=16, diff --git a/apps/accounts/models/automations/gather_account.py b/apps/accounts/models/automations/gather_account.py index 1dd5500f7..ced7995c7 100644 --- a/apps/accounts/models/automations/gather_account.py +++ b/apps/accounts/models/automations/gather_account.py @@ -1,9 +1,35 @@ from django.utils.translation import ugettext_lazy as _ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orgs.mixins.models import JMSOrgBaseModel from accounts.const import AutomationTypes from .base import AccountBaseAutomation -__all__ = ['GatherAccountsAutomation'] +__all__ = ['GatherAccountsAutomation', 'GatheredAccount'] + + +class GatheredAccount(JMSOrgBaseModel): + present = models.BooleanField(default=True, verbose_name=_("Present")) + date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login")) + asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset")) + username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username')) + address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address last login")) + + @property + def address(self): + return self.asset.address + + class Meta: + verbose_name = _('Gather account') + unique_together = [ + ('username', 'asset'), + ] + ordering = ['asset'] + + def __str__(self): + return '{}: {}'.format(self.asset, self.username) class GatherAccountsAutomation(AccountBaseAutomation): diff --git a/apps/accounts/models/automations/push_account.py b/apps/accounts/models/automations/push_account.py index 4e4d95d9d..411e8cb12 100644 --- a/apps/accounts/models/automations/push_account.py +++ b/apps/accounts/models/automations/push_account.py @@ -9,7 +9,6 @@ __all__ = ['PushAccountAutomation'] class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation): - accounts = None triggers = models.JSONField(max_length=16, default=list, verbose_name=_('Triggers')) username = models.CharField(max_length=128, verbose_name=_('Username')) action = models.CharField(max_length=16, verbose_name=_('Action')) diff --git a/apps/accounts/models/gathered_account.py b/apps/accounts/models/gathered_account.py deleted file mode 100644 index e61487d61..000000000 --- a/apps/accounts/models/gathered_account.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.models import JMSOrgBaseModel - -__all__ = ['GatheredAccount'] - - -class GatheredAccount(JMSOrgBaseModel): - present = models.BooleanField(default=True, verbose_name=_("Present")) - date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login")) - asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset")) - username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username')) - address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address last login")) - - @property - def address(self): - return self.asset.address - - class Meta: - verbose_name = _('Gather account') - unique_together = [ - ('username', 'asset'), - ] - ordering = ['asset'] - - def __str__(self): - return '{}: {}'.format(self.asset, self.username) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 1e22a2f16..1f9c143bd 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -3,7 +3,7 @@ from rest_framework import serializers from accounts.const import SecretType, Source from accounts.models import Account, AccountTemplate -from accounts.tasks import push_accounts_to_assets +from accounts.tasks import push_accounts_to_assets_task from assets.const import Category, AllTypes from assets.models import Asset from common.serializers import SecretReadableMixin, BulkModelSerializer @@ -43,7 +43,7 @@ class AccountSerializerCreateValidateMixin: def push_account(instance, push_now): if not push_now: return - push_accounts_to_assets.delay([instance.id], [instance.asset_id]) + push_accounts_to_assets_task.delay([instance.id], [instance.asset_id]) def create(self, validated_data): push_now = validated_data.pop('push_now', None) @@ -140,6 +140,11 @@ class AccountHistorySerializer(serializers.ModelSerializer): class AccountTaskSerializer(serializers.Serializer): ACTION_CHOICES = ( ('test', 'test'), + ('verify', 'verify'), + ('push', 'push'), ) action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True) + accounts = serializers.PrimaryKeyRelatedField( + queryset=Account.objects, required=False, allow_empty=True, many=True + ) task = serializers.CharField(read_only=True) diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py index 0939558e1..b8b5339d7 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,15 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe 'executed_amount': {'label': _('Executed amount')}, } + def validate_name(self, 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 aad6041ba..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, @@ -7,58 +7,16 @@ from .change_secret import ( class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer): - # dynamic_username = serializers.BooleanField(label=_('Dynamic username'), default=False) - # triggers = TreeChoicesField( - # choice_cls=TriggerChoice, label=_('Triggers'), - # default=TriggerChoice.all(), - # ) - # action = LabeledChoiceField( - # choices=PushAccountActionChoice.choices, label=_('Action'), - # default=PushAccountActionChoice.create_and_push - # ) - class Meta(ChangeSecretAutomationSerializer.Meta): model = PushAccountAutomation - fields = copy.copy(ChangeSecretAutomationSerializer.Meta.fields) - fields.remove('recipients') + fields = [ + n for n in ChangeSecretAutomationSerializer.Meta.fields + if n not in ['recipients'] + ] - # fields = ChangeSecretAutomationSerializer.Meta.fields + [ - # 'dynamic_username', 'triggers', 'action' - # ] - - # def validate_username(self, value): - # if self.initial_data.get('dynamic_username'): - # value = '@USER' - # queryset = self.Meta.model.objects.filter(username=value) - # if self.instance: - # queryset = queryset.exclude(id=self.instance.id) - # if queryset.exists(): - # raise serializers.ValidationError(_('Username already exists')) - # return value - # - # def validate_dynamic_username(self, value): - # if not value: - # return value - # queryset = self.Meta.model.objects.filter(username='@USER') - # if self.instance: - # queryset = queryset.exclude(id=self.instance.id) - # if queryset.exists(): - # raise serializers.ValidationError(_('Dynamic username already exists')) - # return value - # - # def validate_triggers(self, value): - # # Now triggers readonly, set all - # return TriggerChoice.all() - # - # def get_field_names(self, declared_fields, info): - # fields = super().get_field_names(declared_fields, info) - # excludes = [ - # 'recipients', 'is_periodic', 'interval', 'crontab', - # 'periodic_display', 'assets', 'nodes' - # ] - # fields = [f for f in fields if f not in excludes] - # fields[fields.index('accounts')] = 'username' - # return fields + @property + def model_type(self): + return AutomationTypes.push_account class PushAccountUpdateAssetSerializer(ChangeSecretUpdateAssetSerializer): diff --git a/apps/accounts/tasks/automation.py b/apps/accounts/tasks/automation.py index bed1de77c..2b9f99235 100644 --- a/apps/accounts/tasks/automation.py +++ b/apps/accounts/tasks/automation.py @@ -24,7 +24,7 @@ def task_activity_callback(self, pid, trigger, tp): queue='ansible', verbose_name=_('Account execute automation'), activity_callback=task_activity_callback ) -def execute_automation(pid, trigger, tp): +def execute_account_automation_task(pid, trigger, tp): model = AutomationTypes.get_type_model(tp) with tmp_to_root_org(): instance = get_object_or_none(model, pk=pid) diff --git a/apps/accounts/tasks/backup_account.py b/apps/accounts/tasks/backup_account.py index 446ce0374..1f4e46d83 100644 --- a/apps/accounts/tasks/backup_account.py +++ b/apps/accounts/tasks/backup_account.py @@ -10,6 +10,7 @@ logger = get_logger(__file__) def task_activity_callback(self, pid, trigger): + from accounts.models import AccountBackupAutomation with tmp_to_root_org(): plan = get_object_or_none(AccountBackupAutomation, pk=pid) if not plan: @@ -22,7 +23,7 @@ def task_activity_callback(self, pid, trigger): @shared_task(verbose_name=_('Execute account backup plan'), activity_callback=task_activity_callback) -def execute_account_backup_plan(pid, trigger): +def execute_account_backup_task(pid, trigger): from accounts.models import AccountBackupAutomation with tmp_to_root_org(): plan = get_object_or_none(AccountBackupAutomation, pk=pid) diff --git a/apps/accounts/tasks/common.py b/apps/accounts/tasks/common.py index 5c8aeedf5..4582322c3 100644 --- a/apps/accounts/tasks/common.py +++ b/apps/accounts/tasks/common.py @@ -4,17 +4,14 @@ from assets.tasks.common import generate_automation_execution_data from common.const.choices import Trigger -def automation_execute_start(task_name, tp, task_snapshot=None): +def quickstart_automation_by_snapshot(task_name, tp, task_snapshot=None): from accounts.models import AutomationExecution data = generate_automation_execution_data(task_name, tp, task_snapshot) - while True: - try: - _id = data['id'] - AutomationExecution.objects.get(id=_id) - data['id'] = str(uuid.uuid4()) - except AutomationExecution.DoesNotExist: - break + pk = data['id'] + if AutomationExecution.objects.filter(id=pk).exists(): + data['id'] = str(uuid.uuid4()) + execution = AutomationExecution.objects.create( trigger=Trigger.manual, **data ) diff --git a/apps/accounts/tasks/gather_accounts.py b/apps/accounts/tasks/gather_accounts.py index d0b322b10..ceead3f9d 100644 --- a/apps/accounts/tasks/gather_accounts.py +++ b/apps/accounts/tasks/gather_accounts.py @@ -4,12 +4,12 @@ from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_noop from accounts.const import AutomationTypes -from accounts.tasks.common import automation_execute_start +from accounts.tasks.common import quickstart_automation_by_snapshot from assets.models import Node from common.utils import get_logger from orgs.utils import org_aware_func -__all__ = ['gather_asset_accounts'] +__all__ = ['gather_asset_accounts_task'] logger = get_logger(__name__) @@ -22,14 +22,14 @@ def gather_asset_accounts_util(nodes, task_name): 'nodes': [str(node.id) for node in nodes], } tp = AutomationTypes.verify_account - automation_execute_start(task_name, tp, task_snapshot) + quickstart_automation_by_snapshot(task_name, tp, task_snapshot) @shared_task( queue="ansible", verbose_name=_('Gather asset accounts'), activity_callback=lambda self, node_ids, task_name=None: (node_ids, None) ) -def gather_asset_accounts(node_ids, task_name=None): +def gather_asset_accounts_task(node_ids, task_name=None): if task_name is None: task_name = gettext_noop("Gather assets accounts") diff --git a/apps/accounts/tasks/push_account.py b/apps/accounts/tasks/push_account.py index 7a24e3b45..10dd4ce64 100644 --- a/apps/accounts/tasks/push_account.py +++ b/apps/accounts/tasks/push_account.py @@ -2,45 +2,33 @@ from celery import shared_task from django.utils.translation import gettext_noop, ugettext_lazy as _ from accounts.const import AutomationTypes -from accounts.tasks.common import automation_execute_start +from accounts.tasks.common import quickstart_automation_by_snapshot from common.utils import get_logger -from orgs.utils import org_aware_func logger = get_logger(__file__) __all__ = [ - 'push_accounts_to_assets', + 'push_accounts_to_assets_task', ] -def push_util(account, assets, task_name): - task_snapshot = { - 'secret': account.secret, - 'secret_type': account.secret_type, - 'accounts': [account.username], - 'assets': [str(asset.id) for asset in assets], - } - tp = AutomationTypes.push_account - automation_execute_start(task_name, tp, task_snapshot) - - -@org_aware_func("assets") -def push_accounts_to_assets_util(accounts, assets): - from accounts.models import PushAccountAutomation - - task_name = gettext_noop("Push accounts to assets") - task_name = PushAccountAutomation.generate_unique_name(task_name) - for account in accounts: - push_util(account, assets, task_name) - - @shared_task( queue="ansible", verbose_name=_('Push accounts to assets'), activity_callback=lambda self, account_ids, asset_ids: (account_ids, None) ) -def push_accounts_to_assets(account_ids, asset_ids): - from assets.models import Asset +def push_accounts_to_assets_task(account_ids): + from accounts.models import PushAccountAutomation from accounts.models import Account - assets = Asset.objects.filter(id__in=asset_ids) accounts = Account.objects.filter(id__in=account_ids) - return push_accounts_to_assets_util(accounts, assets) + task_name = gettext_noop("Push accounts to assets") + task_name = PushAccountAutomation.generate_unique_name(task_name) + + for account in accounts: + task_snapshot = { + 'secret': account.secret, + 'secret_type': account.secret_type, + 'accounts': [account.username], + 'assets': [str(account.asset_id)], + } + tp = AutomationTypes.push_account + quickstart_automation_by_snapshot(task_name, tp, task_snapshot) diff --git a/apps/accounts/tasks/verify_account.py b/apps/accounts/tasks/verify_account.py index dcb0cb58f..efc6bb09d 100644 --- a/apps/accounts/tasks/verify_account.py +++ b/apps/accounts/tasks/verify_account.py @@ -1,16 +1,16 @@ from celery import shared_task -from django.utils.translation import gettext_noop from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_noop from accounts.const import AutomationTypes -from accounts.tasks.common import automation_execute_start +from accounts.tasks.common import quickstart_automation_by_snapshot from assets.const import GATEWAY_NAME from common.utils import get_logger from orgs.utils import org_aware_func logger = get_logger(__name__) __all__ = [ - 'verify_accounts_connectivity' + 'verify_accounts_connectivity_task' ] @@ -22,19 +22,26 @@ def verify_connectivity_util(assets, tp, accounts, task_name): 'accounts': account_usernames, 'assets': [str(asset.id) for asset in assets], } - automation_execute_start(task_name, tp, task_snapshot) + quickstart_automation_by_snapshot(task_name, tp, task_snapshot) @org_aware_func("assets") -def verify_accounts_connectivity_util(accounts, assets, task_name): - gateway_assets = assets.filter(platform__name=GATEWAY_NAME) +def verify_accounts_connectivity_util(accounts, task_name): + from assets.models import Asset + + asset_ids = [a.asset_id for a in accounts] + assets = Asset.objects.filter(id__in=asset_ids) + + gateways = assets.filter(platform__name=GATEWAY_NAME) verify_connectivity_util( - gateway_assets, AutomationTypes.verify_gateway_account, accounts, task_name + gateways, AutomationTypes.verify_gateway_account, + accounts, task_name ) - non_gateway_assets = assets.exclude(platform__name=GATEWAY_NAME) + common_assets = assets.exclude(platform__name=GATEWAY_NAME) verify_connectivity_util( - non_gateway_assets, AutomationTypes.verify_account, accounts, task_name + common_assets, AutomationTypes.verify_account, + accounts, task_name ) @@ -42,11 +49,9 @@ def verify_accounts_connectivity_util(accounts, assets, task_name): queue="ansible", verbose_name=_('Verify asset account availability'), activity_callback=lambda self, account_ids, asset_ids: (account_ids, None) ) -def verify_accounts_connectivity(account_ids, asset_ids): - from assets.models import Asset +def verify_accounts_connectivity_task(account_ids): from accounts.models import Account, VerifyAccountAutomation - assets = Asset.objects.filter(id__in=asset_ids) accounts = Account.objects.filter(id__in=account_ids) task_name = gettext_noop("Verify accounts connectivity") task_name = VerifyAccountAutomation.generate_unique_name(task_name) - return verify_accounts_connectivity_util(accounts, assets, task_name) + return verify_accounts_connectivity_util(accounts, task_name) diff --git a/apps/accounts/urls.py b/apps/accounts/urls.py index 8a28bc0d7..59a70b3cf 100644 --- a/apps/accounts/urls.py +++ b/apps/accounts/urls.py @@ -25,17 +25,22 @@ router.register(r'push-account-executions', api.PushAccountExecutionViewSet, 'pu router.register(r'push-account-records', api.PushAccountRecordViewSet, 'push-account-record') urlpatterns = [ - path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'), - path('account-secrets//histories/', api.AccountHistoriesSecretAPI.as_view(), name='account-secret-history'), + path('accounts/tasks/', api.AccountsTaskCreateAPI.as_view(), name='account-task-create'), + path('account-secrets//histories/', api.AccountHistoriesSecretAPI.as_view(), + name='account-secret-history'), - path('change-secret//asset/remove/', api.ChangSecretRemoveAssetApi.as_view(), name='change-secret-remove-asset'), + path('change-secret//asset/remove/', api.ChangSecretRemoveAssetApi.as_view(), + name='change-secret-remove-asset'), path('change-secret//asset/add/', api.ChangSecretAddAssetApi.as_view(), name='change-secret-add-asset'), - path('change-secret//nodes/', api.ChangSecretNodeAddRemoveApi.as_view(), name='change-secret-add-or-remove-node'), + path('change-secret//nodes/', api.ChangSecretNodeAddRemoveApi.as_view(), + name='change-secret-add-or-remove-node'), path('change-secret//assets/', api.ChangSecretAssetsListApi.as_view(), name='change-secret-assets'), - path('push-account//asset/remove/', api.PushAccountRemoveAssetApi.as_view(), name='push-account-remove-asset'), + path('push-account//asset/remove/', api.PushAccountRemoveAssetApi.as_view(), + name='push-account-remove-asset'), path('push-accountt//asset/add/', api.PushAccountAddAssetApi.as_view(), name='push-account-add-asset'), - path('push-account//nodes/', api.PushAccountNodeAddRemoveApi.as_view(), name='push-account-add-or-remove-node'), + path('push-account//nodes/', api.PushAccountNodeAddRemoveApi.as_view(), + name='push-account-add-or-remove-node'), path('push-account//assets/', api.PushAccountAssetsListApi.as_view(), name='push-account-assets'), ] diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 630a24552..5b6b9084b 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext as _ from rest_framework.decorators import action from rest_framework.response import Response -from accounts.tasks import push_accounts_to_assets, verify_accounts_connectivity +from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task from assets import serializers from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend from assets.models import Asset, Gateway @@ -180,9 +180,9 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): def check_permissions(self, request): action_perm_require = { "refresh": "assets.refresh_assethardwareinfo", - "push_account": "accounts.add_pushaccountexecution", + "push_account": "accounts.push_account", "test": "assets.test_assetconnectivity", - "test_account": "assets.test_account", + "test_account": "accounts.verify_account", } _action = request.data.get("action") perm_required = action_perm_require.get(_action) @@ -205,9 +205,9 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): asset_ids = [asset.id] account_ids = accounts.values_list("id", flat=True) if action == "push_account": - task = push_accounts_to_assets.delay(account_ids, asset_ids) + task = push_accounts_to_assets_task.delay(account_ids, asset_ids) elif action == "test_account": - task = verify_accounts_connectivity.delay(account_ids, asset_ids) + task = verify_accounts_connectivity_task.delay(account_ids, asset_ids) else: task = None return task diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 3658a29f1..47ac0b4e6 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -103,7 +103,7 @@ class NodeAddAssetsApi(generics.UpdateAPIView): instance = None permission_classes = (RBACPermission,) rbac_perms = { - 'PUT': 'assets.add_assettonode', + 'PUT': 'assets.change_assetnodes', } def perform_update(self, serializer): @@ -118,7 +118,7 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView): instance = None permission_classes = (RBACPermission,) rbac_perms = { - 'PUT': 'assets.remove_assetfromnode', + 'PUT': 'assets.change_assetnodes', } def perform_update(self, serializer): @@ -140,7 +140,7 @@ class MoveAssetsToNodeApi(generics.UpdateAPIView): instance = None permission_classes = (RBACPermission,) rbac_perms = { - 'PUT': 'assets.move_assettonode', + 'PUT': 'assets.change_assetnodes', } def perform_update(self, serializer): diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index cece49de9..7ef2e528c 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -66,6 +66,8 @@ class BasePlaybookManager: ) if not os.path.exists(path): os.makedirs(path, exist_ok=True, mode=0o755) + if settings.DEBUG_DEV: + logger.debug('Ansible runtime dir: {}'.format(path)) return path @staticmethod @@ -175,6 +177,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 @@ -279,7 +287,6 @@ class BasePlaybookManager: print(">>> 开始执行任务\n") else: print("### 没有需要执行的任务\n") - return self.execution.date_start = timezone.now() for i, runner in enumerate(runners, start=1): @@ -291,8 +298,9 @@ class BasePlaybookManager: self.on_runner_success(runner, cb) except Exception as e: self.on_runner_failed(runner, e) - self.after_runner_end(runner) - print('\n') + finally: + self.after_runner_end(runner) + print('\n') self.execution.status = 'success' self.execution.date_finished = timezone.now() self.execution.save() diff --git a/apps/assets/automations/ping_gateway/manager.py b/apps/assets/automations/ping_gateway/manager.py index d03e5d22d..b3f243fdb 100644 --- a/apps/assets/automations/ping_gateway/manager.py +++ b/apps/assets/automations/ping_gateway/manager.py @@ -33,7 +33,7 @@ class PingGatewayManager: err = _('No account') return False, err - print('Test account: {}'.format(account)) + print('- ' + _('Asset, {}, using account {}').format(gateway, account)) try: proxy.connect( gateway.address, 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/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/migrations/0108_alter_platform_charset.py b/apps/assets/migrations/0108_alter_platform_charset.py index 9fd978466..d413068f1 100644 --- a/apps/assets/migrations/0108_alter_platform_charset.py +++ b/apps/assets/migrations/0108_alter_platform_charset.py @@ -15,4 +15,29 @@ class Migration(migrations.Migration): name='charset', field=models.CharField(choices=[('utf-8', 'UTF-8'), ('gbk', 'GBK')], default='utf-8', max_length=8, verbose_name='Charset'), ), + migrations.AddField( + model_name='platform', + name='created_by', + field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'), + ), + migrations.AddField( + model_name='platform', + name='date_created', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created'), + ), + migrations.AddField( + model_name='platform', + name='date_updated', + field=models.DateTimeField(auto_now=True, verbose_name='Date updated'), + ), + migrations.AddField( + model_name='platform', + name='updated_by', + field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'), + ), + migrations.AlterField( + model_name='platform', + name='comment', + field=models.TextField(blank=True, default='', verbose_name='Comment'), + ), ] diff --git a/apps/assets/migrations/0109_alter_asset_options.py b/apps/assets/migrations/0109_alter_asset_options.py index 3b9bc25a2..859b1ca0c 100644 --- a/apps/assets/migrations/0109_alter_asset_options.py +++ b/apps/assets/migrations/0109_alter_asset_options.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='asset', - options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetaccount', 'Can push account to asset'), ('test_account', 'Can verify account'), ('match_asset', 'Can match asset'), ('add_assettonode', 'Add asset to node'), ('move_assettonode', 'Move asset to node'), ('remove_assetfromnode', 'Remove asset from node')], 'verbose_name': 'Asset'}, + options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetaccount', 'Can push account to asset'), ('test_account', 'Can verify account'), ('match_asset', 'Can match asset'), ('change_assettonode', 'Can change asset nodes')], 'verbose_name': 'Asset'}, ), ] diff --git a/apps/assets/migrations/0110_alter_asset_options.py b/apps/assets/migrations/0110_alter_asset_options.py new file mode 100644 index 000000000..fa5495e33 --- /dev/null +++ b/apps/assets/migrations/0110_alter_asset_options.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.14 on 2023-02-21 05:11 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0109_alter_asset_options'), + ] + + operations = [ + migrations.AlterModelOptions( + name='asset', + options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'}, + ), + ] diff --git a/apps/assets/migrations/0110_auto_20230220_1051.py b/apps/assets/migrations/0110_auto_20230220_1051.py deleted file mode 100644 index a94fd7d03..000000000 --- a/apps/assets/migrations/0110_auto_20230220_1051.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.14 on 2023-02-20 02:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0109_alter_asset_options'), - ] - - operations = [ - migrations.AddField( - model_name='platform', - name='created_by', - field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'), - ), - migrations.AddField( - model_name='platform', - name='date_created', - field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created'), - ), - migrations.AddField( - model_name='platform', - name='date_updated', - field=models.DateTimeField(auto_now=True, verbose_name='Date updated'), - ), - migrations.AddField( - model_name='platform', - name='updated_by', - field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'), - ), - migrations.AlterField( - model_name='platform', - name='comment', - field=models.TextField(blank=True, default='', verbose_name='Comment'), - ), - ] diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 8fd2d8a2c..8460556c4 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -281,10 +281,6 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): permissions = [ ('refresh_assethardwareinfo', _('Can refresh asset hardware info')), ('test_assetconnectivity', _('Can test asset connectivity')), - ('push_assetaccount', _('Can push account to asset')), - ('test_account', _('Can verify account')), ('match_asset', _('Can match asset')), - ('add_assettonode', _('Add asset to node')), - ('move_assettonode', _('Move asset to node')), - ('remove_assetfromnode', _('Remove asset from node')) + ('change_assetnodes', _('Can change asset nodes')), ] diff --git a/apps/assets/models/automations/base.py b/apps/assets/models/automations/base.py index 423ba9d09..9ddeb5bf2 100644 --- a/apps/assets/models/automations/base.py +++ b/apps/assets/models/automations/base.py @@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy as _ from assets.models.asset import Asset from assets.models.node import Node -from assets.tasks import execute_automation +from assets.tasks import execute_asset_automation_task from common.const.choices import Trigger from common.db.fields import EncryptJsonDictTextField from ops.mixin import PeriodTaskModelMixin @@ -49,7 +49,7 @@ class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel): @property def execute_task(self): - return execute_automation + return execute_asset_automation_task def get_register_task(self): name = f"automation_{self.type}_strategy_period_{str(self.id)[:8]}" diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index dfb0e32fe..6dd5e3c4f 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -30,11 +30,11 @@ class Domain(JMSOrgBaseModel): def random_gateway(self): gateways = [gw for gw in self.active_gateways if gw.is_connective] + if not gateways: - logger.warn(f'Gateway all bad. domain={self}, gateway_num={len(gateways)}.') gateways = self.active_gateways if not gateways: - logger.warn(f'Not active gateway. domain={self}') + logger.warn(f'Not active gateway, domain={self}, pass') return None return random.choice(gateways) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 0d4939086..9c31ee695 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -83,7 +83,7 @@ class AssetAccountSerializer( def validate_push_now(self, value): request = self.context['request'] - if not request.user.has_perms('assets.push_assetaccount'): + if not request.user.has_perms('accounts.push_account'): return False return value diff --git a/apps/assets/tasks/automation.py b/apps/assets/tasks/automation.py index 4e7d340c2..02e946ede 100644 --- a/apps/assets/tasks/automation.py +++ b/apps/assets/tasks/automation.py @@ -1,9 +1,9 @@ from celery import shared_task from django.utils.translation import gettext_lazy as _ -from orgs.utils import tmp_to_root_org, tmp_to_org -from common.utils import get_logger, get_object_or_none from assets.const import AutomationTypes +from common.utils import get_logger, get_object_or_none +from orgs.utils import tmp_to_root_org, tmp_to_org logger = get_logger(__file__) @@ -24,7 +24,7 @@ def task_activity_callback(self, pid, trigger, tp): queue='ansible', verbose_name=_('Asset execute automation'), activity_callback=task_activity_callback ) -def execute_automation(pid, trigger, tp): +def execute_asset_automation_task(pid, trigger, tp): model = AutomationTypes.get_type_model(tp) with tmp_to_root_org(): instance = get_object_or_none(model, pk=pid) 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 f75ed9b82..df0751934 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:8285be68ee7edb5423cf973c1c96a3659b75b40566c07a1557a17d59fcaf294d -size 136019 +oid sha256:331188bb5169bb463da018a635589e12a2136d476db264ac7e5d6e5d63ca474a +size 135916 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index a2156418c..4e86c7f0c 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-20 19:19+0800\n" +"POT-Creation-Date: 2023-02-21 18:29+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -158,11 +158,12 @@ msgstr "作成してプッシュ" msgid "Only create" msgstr "作成のみ" -#: accounts/models/account.py:47 accounts/models/gathered_account.py:14 +#: accounts/models/account.py:47 +#: 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 @@ -181,7 +182,7 @@ msgid "Su from" msgstr "から切り替え" #: accounts/models/account.py:53 settings/serializers/auth/cas.py:20 -#: terminal/models/applet/applet.py:28 +#: terminal/models/applet/applet.py:29 msgid "Version" msgstr "バージョン" @@ -191,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 @@ -214,22 +215,30 @@ msgstr "資産履歴アカウントを表示できます" msgid "Can view asset history account secret" msgstr "資産履歴アカウントパスワードを表示できます" -#: accounts/models/account.py:111 +#: accounts/models/account.py:67 +msgid "Can verify account" +msgstr "アカウントを確認できます" + +#: accounts/models/account.py:68 +msgid "Can push account" +msgstr "アカウントをプッシュできます" + +#: accounts/models/account.py:113 msgid "Account template" msgstr "アカウント テンプレート" -#: accounts/models/account.py:116 +#: accounts/models/account.py:118 msgid "Can view asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを表示できます" -#: accounts/models/account.py:117 +#: accounts/models/account.py:119 msgid "Can change asset account template secret" 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 "受信者" @@ -241,7 +250,7 @@ msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:114 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:122 -#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:108 +#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:109 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 #: tickets/models/ticket/apply_asset.py:19 @@ -260,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" @@ -272,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 "成功は" @@ -327,12 +336,7 @@ msgstr "プッシュ アカウントの作成の実行" msgid "Secret type" msgstr "鍵の種類" -#: accounts/models/automations/change_secret.py:21 -#: accounts/serializers/automations/change_secret.py:40 -msgid "Secret strategy" -msgstr "鍵ポリシー" - -#: accounts/models/automations/change_secret.py:23 +#: accounts/models/automations/change_secret.py:19 #: accounts/models/automations/change_secret.py:72 accounts/models/base.py:38 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -340,6 +344,11 @@ msgstr "鍵ポリシー" msgid "Secret" msgstr "ひみつ" +#: accounts/models/automations/change_secret.py:22 +#: accounts/serializers/automations/change_secret.py:40 +msgid "Secret strategy" +msgstr "鍵ポリシー" + #: accounts/models/automations/change_secret.py:24 msgid "Password rules" msgstr "パスワードルール" @@ -363,7 +372,7 @@ msgstr "開始日" #: accounts/models/automations/change_secret.py:74 #: assets/models/automations/base.py:115 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:123 -#: terminal/models/applet/host.py:109 +#: terminal/models/applet/host.py:110 msgid "Date finished" msgstr "終了日" @@ -376,20 +385,19 @@ msgstr "間違い" msgid "Change secret record" msgstr "パスワード レコードの変更" +#: accounts/models/automations/gather_account.py:14 +msgid "Present" +msgstr "存在する" + #: accounts/models/automations/gather_account.py:15 -#: accounts/tasks/gather_accounts.py:29 -msgid "Gather asset accounts" -msgstr "アカウントのコレクション" +msgid "Date last login" +msgstr "最終ログイン日" -#: accounts/models/automations/push_account.py:13 -msgid "Triggers" -msgstr "トリガー方式" - -#: accounts/models/automations/push_account.py:14 accounts/models/base.py:34 -#: accounts/models/gathered_account.py:15 acls/serializers/base.py:18 -#: acls/serializers/base.py:49 assets/models/_user.py:23 audits/models.py:157 -#: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models/temp_token.py:9 +#: accounts/models/automations/gather_account.py:17 +#: accounts/models/automations/push_account.py:13 accounts/models/base.py:34 +#: acls/serializers/base.py:18 acls/serializers/base.py:49 +#: assets/models/_user.py:23 audits/models.py:157 authentication/forms.py:25 +#: authentication/forms.py:27 authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:112 @@ -398,7 +406,24 @@ msgstr "トリガー方式" msgid "Username" msgstr "ユーザー名" -#: accounts/models/automations/push_account.py:15 acls/models/base.py:81 +#: accounts/models/automations/gather_account.py:18 +msgid "Address last login" +msgstr "最終ログインアドレス" + +#: accounts/models/automations/gather_account.py:25 rbac/tree.py:50 +msgid "Gather account" +msgstr "アカウントを集める" + +#: accounts/models/automations/gather_account.py:41 +#: accounts/tasks/gather_accounts.py:29 +msgid "Gather asset accounts" +msgstr "アカウントのコレクション" + +#: accounts/models/automations/push_account.py:12 +msgid "Triggers" +msgstr "トリガー方式" + +#: accounts/models/automations/push_account.py:14 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 @@ -406,7 +431,7 @@ msgstr "ユーザー名" msgid "Action" msgstr "アクション" -#: accounts/models/automations/push_account.py:41 +#: accounts/models/automations/push_account.py:40 msgid "Push asset account" msgstr "アカウントプッシュ" @@ -422,13 +447,13 @@ msgstr "アカウントの確認" #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:21 assets/models/platform.py:76 #: assets/serializers/asset/common.py:68 assets/serializers/asset/common.py:142 -#: assets/serializers/platform.py:132 +#: assets/serializers/platform.py:91 assets/serializers/platform.py:136 #: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:26 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 #: settings/models.py:33 settings/serializers/sms.py:6 -#: terminal/models/applet/applet.py:26 terminal/models/component/endpoint.py:12 +#: 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:79 users/forms/profile.py:33 @@ -445,26 +470,10 @@ msgstr "特権アカウント" #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:107 -#: terminal/models/applet/applet.py:31 users/serializers/user.py:159 +#: terminal/models/applet/applet.py:32 users/serializers/user.py:159 msgid "Is active" msgstr "アクティブです。" -#: accounts/models/gathered_account.py:12 -msgid "Present" -msgstr "存在する" - -#: accounts/models/gathered_account.py:13 -msgid "Date last login" -msgstr "最終ログイン日" - -#: accounts/models/gathered_account.py:16 -msgid "Address last login" -msgstr "最終ログインアドレス" - -#: accounts/models/gathered_account.py:23 rbac/tree.py:50 -msgid "Gather account" -msgstr "アカウントを集める" - #: accounts/notifications.py:8 msgid "Notification of account backup route task results" msgstr "アカウントバックアップルートタスクの結果の通知" @@ -524,21 +533,21 @@ msgstr "エスクローされたパスワード" #: accounts/serializers/account/account.py:75 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:77 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:93 assets/serializers/platform.py:133 +#: assets/serializers/platform.py:97 assets/serializers/platform.py:137 #: perms/serializers/user_permission.py:25 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 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 -#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:92 +#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:96 #: audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:37 -#: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:30 +#: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:28 #: terminal/serializers/session.py:22 terminal/serializers/storage.py:224 @@ -566,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 "現在、メール送信のみがサポートされています" @@ -603,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" @@ -612,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 @@ -633,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" @@ -643,7 +656,7 @@ msgstr "失敗しました" msgid "Account execute automation" msgstr "アカウント実行の自動化" -#: accounts/tasks/backup_account.py:24 +#: accounts/tasks/backup_account.py:25 msgid "Execute account backup plan" msgstr "アカウントのバックアップ計画を実施する" @@ -651,15 +664,15 @@ msgstr "アカウントのバックアップ計画を実施する" msgid "Gather assets accounts" msgstr "資産の口座番号を収集する" -#: accounts/tasks/push_account.py:30 accounts/tasks/push_account.py:37 +#: accounts/tasks/push_account.py:15 accounts/tasks/push_account.py:23 msgid "Push accounts to assets" msgstr "アカウントをアセットにプッシュ:" -#: accounts/tasks/verify_account.py:42 +#: accounts/tasks/verify_account.py:49 msgid "Verify asset account availability" msgstr "アセット アカウントの可用性を確認する" -#: accounts/tasks/verify_account.py:50 +#: accounts/tasks/verify_account.py:55 msgid "Verify accounts connectivity" msgstr "アカウント接続のテスト" @@ -921,7 +934,7 @@ msgstr "削除に失敗し、ノードにアセットが含まれています。 msgid "App assets" msgstr "アプリ資産" -#: assets/automations/base/manager.py:105 +#: assets/automations/base/manager.py:107 msgid "{} disabled" msgstr "{} 無効" @@ -930,6 +943,12 @@ msgstr "{} 無効" msgid "No account" msgstr "アカウントなし" +#: assets/automations/ping_gateway/manager.py:36 +#, fuzzy +#| msgid "Assets amount" +msgid "Asset, {}, using account {}" +msgstr "資産額" + #: assets/automations/ping_gateway/manager.py:55 #, python-brace-format msgid "Unable to connect to port {port} on {address}" @@ -988,11 +1007,11 @@ msgid "Cloud service" msgstr "クラウド サービス" #: assets/const/category.py:15 assets/models/asset/web.py:16 audits/const.py:33 -#: terminal/models/applet/applet.py:24 +#: terminal/models/applet/applet.py:25 msgid "Web" msgstr "Web" -#: assets/const/device.py:7 terminal/models/applet/applet.py:23 +#: assets/const/device.py:7 terminal/models/applet/applet.py:24 #: tickets/const.py:8 msgid "General" msgstr "一般" @@ -1028,7 +1047,7 @@ msgid "Basic" msgstr "基本" #: assets/const/web.py:61 assets/models/asset/web.py:13 -#: assets/serializers/asset/common.py:117 assets/serializers/platform.py:39 +#: assets/serializers/asset/common.py:117 assets/serializers/platform.py:40 msgid "Script" msgstr "脚本" @@ -1044,8 +1063,8 @@ 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:45 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:35 terminal/models/applet/applet.py:151 -#: terminal/models/applet/host.py:110 terminal/models/component/endpoint.py:24 +#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:155 +#: 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 #: tickets/models/ticket/general.py:297 users/models/user.py:756 @@ -1094,7 +1113,7 @@ msgstr "ユーザーと同じユーザー名" #: assets/models/_user.py:52 authentication/models/connection_token.py:38 #: authentication/serializers/connect_token_secret.py:104 -#: terminal/models/applet/applet.py:33 terminal/serializers/session.py:20 +#: terminal/models/applet/applet.py:34 terminal/serializers/session.py:20 #: terminal/serializers/session.py:41 terminal/serializers/storage.py:68 msgid "Protocol" msgstr "プロトコル" @@ -1183,28 +1202,12 @@ msgid "Can test asset connectivity" msgstr "資産接続をテストできます" #: assets/models/asset/common.py:284 -msgid "Can push account to asset" -msgstr "アカウントをアセットにプッシュできます" - -#: assets/models/asset/common.py:285 -msgid "Can verify account" -msgstr "アカウントを確認できます" - -#: assets/models/asset/common.py:286 msgid "Can match asset" msgstr "アセットを一致させることができます" -#: assets/models/asset/common.py:287 -msgid "Add asset to node" -msgstr "ノードにアセットを追加する" - -#: assets/models/asset/common.py:288 -msgid "Move asset to node" -msgstr "アセットをノードに移動する" - -#: assets/models/asset/common.py:289 -msgid "Remove asset from node" -msgstr "ノードからアセットを削除" +#: assets/models/asset/common.py:285 +msgid "Can change asset nodes" +msgstr "資産ノードを変更できます" #: assets/models/asset/database.py:10 assets/serializers/asset/common.py:110 #: settings/serializers/email.py:37 @@ -1227,22 +1230,22 @@ msgstr "クライアントキー" 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:114 -#: assets/serializers/platform.py:31 +#: assets/serializers/platform.py:32 msgid "Username selector" msgstr "ユーザー名ピッカー" #: assets/models/asset/web.py:11 assets/serializers/asset/common.py:115 -#: assets/serializers/platform.py:34 +#: assets/serializers/platform.py:35 msgid "Password selector" msgstr "パスワードセレクター" #: assets/models/asset/web.py:12 assets/serializers/asset/common.py:116 -#: assets/serializers/platform.py:37 +#: assets/serializers/platform.py:38 msgid "Submit selector" msgstr "ボタンセレクターを確認する" @@ -1261,7 +1264,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:114 -#: terminal/models/applet/applet.py:150 terminal/models/applet/host.py:107 +#: terminal/models/applet/applet.py:154 terminal/models/applet/host.py:108 #: terminal/models/component/status.py:27 terminal/serializers/applet.py:17 #: terminal/serializers/applet_host.py:93 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1403,45 +1406,45 @@ msgstr "有効化" msgid "Ansible config" msgstr "Ansible 構成" -#: assets/models/platform.py:44 assets/serializers/platform.py:60 +#: assets/models/platform.py:44 assets/serializers/platform.py:61 msgid "Ping enabled" msgstr "アセット ディスカバリを有効にする" -#: assets/models/platform.py:45 assets/serializers/platform.py:61 +#: assets/models/platform.py:45 assets/serializers/platform.py:62 msgid "Ping method" msgstr "資産検出方法" #: assets/models/platform.py:46 assets/models/platform.py:59 -#: assets/serializers/platform.py:62 +#: assets/serializers/platform.py:63 msgid "Gather facts enabled" msgstr "資産情報の収集を有効にする" #: assets/models/platform.py:47 assets/models/platform.py:61 -#: assets/serializers/platform.py:63 +#: assets/serializers/platform.py:64 msgid "Gather facts method" msgstr "情報収集の方法" -#: assets/models/platform.py:48 assets/serializers/platform.py:66 +#: assets/models/platform.py:48 assets/serializers/platform.py:67 msgid "Change secret enabled" msgstr "パスワードの変更が有効" -#: assets/models/platform.py:50 assets/serializers/platform.py:67 +#: assets/models/platform.py:50 assets/serializers/platform.py:68 msgid "Change secret method" msgstr "パスワード変更モード" -#: assets/models/platform.py:52 assets/serializers/platform.py:68 +#: assets/models/platform.py:52 assets/serializers/platform.py:69 msgid "Push account enabled" msgstr "アカウントのプッシュを有効にする" -#: assets/models/platform.py:54 assets/serializers/platform.py:69 +#: assets/models/platform.py:54 assets/serializers/platform.py:70 msgid "Push account method" msgstr "アカウントプッシュ方式" -#: assets/models/platform.py:56 assets/serializers/platform.py:64 +#: assets/models/platform.py:56 assets/serializers/platform.py:65 msgid "Verify account enabled" msgstr "アカウントの確認をオンにする" -#: assets/models/platform.py:58 assets/serializers/platform.py:65 +#: assets/models/platform.py:58 assets/serializers/platform.py:66 msgid "Verify account method" msgstr "アカウント認証方法" @@ -1453,23 +1456,23 @@ msgstr "メタ" msgid "Internal" msgstr "ビルトイン" -#: assets/models/platform.py:83 assets/serializers/platform.py:90 +#: assets/models/platform.py:83 assets/serializers/platform.py:94 msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:85 assets/serializers/platform.py:118 +#: assets/models/platform.py:85 assets/serializers/platform.py:122 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:87 assets/serializers/platform.py:117 +#: assets/models/platform.py:87 assets/serializers/platform.py:121 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" -#: assets/models/platform.py:88 assets/serializers/platform.py:100 +#: assets/models/platform.py:88 assets/serializers/platform.py:104 msgid "Su method" msgstr "アカウントの切り替え方法" -#: assets/models/platform.py:90 assets/serializers/platform.py:97 +#: assets/models/platform.py:90 assets/serializers/platform.py:101 msgid "Automation" msgstr "オートメーション" @@ -1482,7 +1485,7 @@ msgstr "%(value)s は偶数ではありません" msgid "Auto fill" msgstr "自動充填" -#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:95 +#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:99 #: authentication/serializers/connect_token_secret.py:28 #: authentication/serializers/connect_token_secret.py:66 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:99 @@ -1594,31 +1597,31 @@ 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 ルート パス" -#: assets/serializers/platform.py:42 +#: assets/serializers/platform.py:43 msgid "Auth with username" msgstr "ユーザー名で認証する" -#: assets/serializers/platform.py:70 +#: assets/serializers/platform.py:71 msgid "Gather accounts enabled" msgstr "アカウント収集を有効にする" -#: assets/serializers/platform.py:71 +#: assets/serializers/platform.py:72 msgid "Gather accounts method" msgstr "アカウントの収集方法" -#: assets/serializers/platform.py:77 +#: assets/serializers/platform.py:78 msgid "Primary" msgstr "主要" -#: assets/serializers/platform.py:119 +#: assets/serializers/platform.py:123 msgid "Default Domain" msgstr "デフォルト ドメイン" @@ -1762,7 +1765,7 @@ msgid "Change password" msgstr "パスワードを変更する" #: audits/const.py:34 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:24 terminal/models/component/terminal.py:156 +#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:156 #: terminal/serializers/session.py:48 msgid "Terminal" msgstr "ターミナル" @@ -1779,7 +1782,7 @@ msgstr "セッションログ" msgid "Login log" msgstr "ログインログ" -#: audits/const.py:42 terminal/models/applet/host.py:111 +#: audits/const.py:42 terminal/models/applet/host.py:112 #: terminal/models/component/task.py:24 msgid "Task" msgstr "タスク" @@ -3587,7 +3590,7 @@ msgstr "組織" msgid "Org name" msgstr "組織名" -#: orgs/models.py:70 rbac/models/role.py:36 terminal/models/applet/applet.py:32 +#: orgs/models.py:70 rbac/models/role.py:36 terminal/models/applet/applet.py:33 msgid "Builtin" msgstr "ビルトイン" @@ -3832,7 +3835,7 @@ msgstr "パーマ" msgid "Users amount" msgstr "ユーザー数" -#: rbac/serializers/role.py:28 terminal/models/applet/applet.py:27 +#: rbac/serializers/role.py:28 terminal/models/applet/applet.py:28 msgid "Display name" msgstr "表示名" @@ -3896,8 +3899,8 @@ msgstr "タスクセンター" msgid "My assets" msgstr "私の資産" -#: rbac/tree.py:57 terminal/models/applet/applet.py:42 -#: terminal/models/applet/applet.py:147 terminal/models/applet/host.py:27 +#: rbac/tree.py:57 terminal/models/applet/applet.py:43 +#: terminal/models/applet/applet.py:151 terminal/models/applet/host.py:28 msgid "Applet" msgstr "リモートアプリケーション" @@ -5421,44 +5424,44 @@ msgstr "一括作成非サポート" msgid "Storage is invalid" msgstr "ストレージが無効です" -#: terminal/models/applet/applet.py:29 +#: terminal/models/applet/applet.py:30 msgid "Author" msgstr "著者" -#: terminal/models/applet/applet.py:34 +#: terminal/models/applet/applet.py:35 msgid "Tags" msgstr "ラベル" -#: terminal/models/applet/applet.py:38 terminal/serializers/storage.py:157 +#: terminal/models/applet/applet.py:39 terminal/serializers/storage.py:157 msgid "Hosts" msgstr "ホスト" -#: terminal/models/applet/applet.py:83 +#: terminal/models/applet/applet.py:84 msgid "Applet pkg not valid, Missing file {}" msgstr "無効なアプレット パッケージ、ファイル {} がありません" -#: terminal/models/applet/applet.py:149 terminal/models/applet/host.py:33 -#: terminal/models/applet/host.py:105 +#: terminal/models/applet/applet.py:153 terminal/models/applet/host.py:34 +#: terminal/models/applet/host.py:106 msgid "Hosting" msgstr "ホスト マシン" -#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:43 +#: terminal/models/applet/host.py:19 terminal/serializers/applet_host.py:43 msgid "Deploy options" msgstr "展開パラメーター" -#: terminal/models/applet/host.py:19 +#: terminal/models/applet/host.py:20 msgid "Inited" msgstr "初期化された" -#: terminal/models/applet/host.py:20 +#: terminal/models/applet/host.py:21 msgid "Date inited" msgstr "" -#: terminal/models/applet/host.py:21 +#: terminal/models/applet/host.py:22 msgid "Date synced" msgstr "同期日" -#: terminal/models/applet/host.py:106 +#: terminal/models/applet/host.py:107 msgid "Initial" msgstr "初期化" @@ -7362,3 +7365,15 @@ msgstr "究極のエディション" #: xpack/plugins/license/models.py:85 msgid "Community edition" msgstr "コミュニティ版" + +#~ msgid "Can push account to asset" +#~ msgstr "アカウントをアセットにプッシュできます" + +#~ msgid "Add asset to node" +#~ msgstr "ノードにアセットを追加する" + +#~ msgid "Move asset to node" +#~ msgstr "アセットをノードに移動する" + +#~ msgid "Remove asset from node" +#~ msgstr "ノードからアセットを削除" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index e462b3e98..cd68677c8 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:3a10e56f57d89f413c4aac8026e4339f630f70f6a66df8b9edee5b39bed88a75 -size 111742 +oid sha256:2cdc2b875c98f41bd698833a989195d8cc4245f39f52b7eab41ad4d95075cb17 +size 111666 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 6c9b0c6c7..dc0f1de28 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-20 19:19+0800\n" +"POT-Creation-Date: 2023-02-21 18:29+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -157,11 +157,12 @@ msgstr "创建并推送" msgid "Only create" msgstr "仅创建" -#: accounts/models/account.py:47 accounts/models/gathered_account.py:14 +#: accounts/models/account.py:47 +#: 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 @@ -180,7 +181,7 @@ msgid "Su from" msgstr "切换自" #: accounts/models/account.py:53 settings/serializers/auth/cas.py:20 -#: terminal/models/applet/applet.py:28 +#: terminal/models/applet/applet.py:29 msgid "Version" msgstr "版本" @@ -190,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 @@ -213,22 +214,30 @@ msgstr "可以查看资产历史账号" msgid "Can view asset history account secret" msgstr "可以查看资产历史账号密码" -#: accounts/models/account.py:111 +#: accounts/models/account.py:67 +msgid "Can verify account" +msgstr "可以验证账号" + +#: accounts/models/account.py:68 +msgid "Can push account" +msgstr "可以推送账号" + +#: accounts/models/account.py:113 msgid "Account template" msgstr "账号模版" -#: accounts/models/account.py:116 +#: accounts/models/account.py:118 msgid "Can view asset account template secret" msgstr "可以查看资产账号模版密码" -#: accounts/models/account.py:117 +#: accounts/models/account.py:119 msgid "Can change asset account template secret" 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 "收件人" @@ -240,7 +249,7 @@ msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:114 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:122 -#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:108 +#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:109 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 #: tickets/models/ticket/apply_asset.py:19 @@ -259,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" @@ -271,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 "是否成功" @@ -326,12 +335,7 @@ msgstr "创建推送账号执行" msgid "Secret type" msgstr "密文类型" -#: accounts/models/automations/change_secret.py:21 -#: accounts/serializers/automations/change_secret.py:40 -msgid "Secret strategy" -msgstr "密文策略" - -#: accounts/models/automations/change_secret.py:23 +#: accounts/models/automations/change_secret.py:19 #: accounts/models/automations/change_secret.py:72 accounts/models/base.py:38 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -339,6 +343,11 @@ msgstr "密文策略" msgid "Secret" msgstr "密钥" +#: accounts/models/automations/change_secret.py:22 +#: accounts/serializers/automations/change_secret.py:40 +msgid "Secret strategy" +msgstr "密文策略" + #: accounts/models/automations/change_secret.py:24 msgid "Password rules" msgstr "密码规则" @@ -362,7 +371,7 @@ msgstr "开始日期" #: accounts/models/automations/change_secret.py:74 #: assets/models/automations/base.py:115 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:123 -#: terminal/models/applet/host.py:109 +#: terminal/models/applet/host.py:110 msgid "Date finished" msgstr "结束日期" @@ -375,20 +384,19 @@ msgstr "错误" msgid "Change secret record" msgstr "改密记录" +#: accounts/models/automations/gather_account.py:14 +msgid "Present" +msgstr "存在" + #: accounts/models/automations/gather_account.py:15 -#: accounts/tasks/gather_accounts.py:29 -msgid "Gather asset accounts" -msgstr "收集账号" +msgid "Date last login" +msgstr "最后登录日期" -#: accounts/models/automations/push_account.py:13 -msgid "Triggers" -msgstr "触发方式" - -#: accounts/models/automations/push_account.py:14 accounts/models/base.py:34 -#: accounts/models/gathered_account.py:15 acls/serializers/base.py:18 -#: acls/serializers/base.py:49 assets/models/_user.py:23 audits/models.py:157 -#: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models/temp_token.py:9 +#: accounts/models/automations/gather_account.py:17 +#: accounts/models/automations/push_account.py:13 accounts/models/base.py:34 +#: acls/serializers/base.py:18 acls/serializers/base.py:49 +#: assets/models/_user.py:23 audits/models.py:157 authentication/forms.py:25 +#: authentication/forms.py:27 authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:112 @@ -397,7 +405,24 @@ msgstr "触发方式" msgid "Username" msgstr "用户名" -#: accounts/models/automations/push_account.py:15 acls/models/base.py:81 +#: accounts/models/automations/gather_account.py:18 +msgid "Address last login" +msgstr "最后登录地址" + +#: accounts/models/automations/gather_account.py:25 rbac/tree.py:50 +msgid "Gather account" +msgstr "收集账号" + +#: accounts/models/automations/gather_account.py:41 +#: accounts/tasks/gather_accounts.py:29 +msgid "Gather asset accounts" +msgstr "收集账号" + +#: accounts/models/automations/push_account.py:12 +msgid "Triggers" +msgstr "触发方式" + +#: accounts/models/automations/push_account.py:14 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 @@ -405,7 +430,7 @@ msgstr "用户名" msgid "Action" msgstr "动作" -#: accounts/models/automations/push_account.py:41 +#: accounts/models/automations/push_account.py:40 msgid "Push asset account" msgstr "账号推送" @@ -421,13 +446,13 @@ msgstr "账号验证" #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:21 assets/models/platform.py:76 #: assets/serializers/asset/common.py:68 assets/serializers/asset/common.py:142 -#: assets/serializers/platform.py:132 +#: assets/serializers/platform.py:91 assets/serializers/platform.py:136 #: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:26 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 #: settings/models.py:33 settings/serializers/sms.py:6 -#: terminal/models/applet/applet.py:26 terminal/models/component/endpoint.py:12 +#: 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:79 users/forms/profile.py:33 @@ -444,26 +469,10 @@ msgstr "特权账号" #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:107 -#: terminal/models/applet/applet.py:31 users/serializers/user.py:159 +#: terminal/models/applet/applet.py:32 users/serializers/user.py:159 msgid "Is active" msgstr "激活" -#: accounts/models/gathered_account.py:12 -msgid "Present" -msgstr "存在" - -#: accounts/models/gathered_account.py:13 -msgid "Date last login" -msgstr "最后登录日期" - -#: accounts/models/gathered_account.py:16 -msgid "Address last login" -msgstr "最后登录地址" - -#: accounts/models/gathered_account.py:23 rbac/tree.py:50 -msgid "Gather account" -msgstr "收集账号" - #: accounts/notifications.py:8 msgid "Notification of account backup route task results" msgstr "账号备份任务结果通知" @@ -520,21 +529,21 @@ msgstr "已托管密码" #: accounts/serializers/account/account.py:75 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:77 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:93 assets/serializers/platform.py:133 +#: assets/serializers/platform.py:97 assets/serializers/platform.py:137 #: perms/serializers/user_permission.py:25 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 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 -#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:92 +#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:96 #: audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:37 -#: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:30 +#: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:28 #: terminal/serializers/session.py:22 terminal/serializers/storage.py:224 @@ -562,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 "当前只支持邮件发送" @@ -599,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" @@ -608,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 @@ -629,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" @@ -639,7 +652,7 @@ msgstr "失败" msgid "Account execute automation" msgstr "账号执行自动化" -#: accounts/tasks/backup_account.py:24 +#: accounts/tasks/backup_account.py:25 msgid "Execute account backup plan" msgstr "执行账号备份计划" @@ -647,15 +660,15 @@ msgstr "执行账号备份计划" msgid "Gather assets accounts" msgstr "收集资产上的账号" -#: accounts/tasks/push_account.py:30 accounts/tasks/push_account.py:37 +#: accounts/tasks/push_account.py:15 accounts/tasks/push_account.py:23 msgid "Push accounts to assets" msgstr "推送账号到资产" -#: accounts/tasks/verify_account.py:42 +#: accounts/tasks/verify_account.py:49 msgid "Verify asset account availability" msgstr "验证资产账号可用性" -#: accounts/tasks/verify_account.py:50 +#: accounts/tasks/verify_account.py:55 msgid "Verify accounts connectivity" msgstr "测试账号可连接性" @@ -913,7 +926,7 @@ msgstr "删除失败,节点包含资产" msgid "App assets" msgstr "资产管理" -#: assets/automations/base/manager.py:105 +#: assets/automations/base/manager.py:107 msgid "{} disabled" msgstr "{} 已禁用" @@ -922,6 +935,12 @@ msgstr "{} 已禁用" msgid "No account" msgstr "没有账号" +#: assets/automations/ping_gateway/manager.py:36 +#, fuzzy +#| msgid "Assets amount" +msgid "Asset, {}, using account {}" +msgstr "资产数量" + #: assets/automations/ping_gateway/manager.py:55 #, python-brace-format msgid "Unable to connect to port {port} on {address}" @@ -980,11 +999,11 @@ msgid "Cloud service" msgstr "云服务" #: assets/const/category.py:15 assets/models/asset/web.py:16 audits/const.py:33 -#: terminal/models/applet/applet.py:24 +#: terminal/models/applet/applet.py:25 msgid "Web" msgstr "Web" -#: assets/const/device.py:7 terminal/models/applet/applet.py:23 +#: assets/const/device.py:7 terminal/models/applet/applet.py:24 #: tickets/const.py:8 msgid "General" msgstr "一般" @@ -1020,7 +1039,7 @@ msgid "Basic" msgstr "基本" #: assets/const/web.py:61 assets/models/asset/web.py:13 -#: assets/serializers/asset/common.py:117 assets/serializers/platform.py:39 +#: assets/serializers/asset/common.py:117 assets/serializers/platform.py:40 msgid "Script" msgstr "脚本" @@ -1036,8 +1055,8 @@ 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:45 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:35 terminal/models/applet/applet.py:151 -#: terminal/models/applet/host.py:110 terminal/models/component/endpoint.py:24 +#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:155 +#: 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 #: tickets/models/ticket/general.py:297 users/models/user.py:756 @@ -1086,7 +1105,7 @@ msgstr "用户名与用户相同" #: assets/models/_user.py:52 authentication/models/connection_token.py:38 #: authentication/serializers/connect_token_secret.py:104 -#: terminal/models/applet/applet.py:33 terminal/serializers/session.py:20 +#: terminal/models/applet/applet.py:34 terminal/serializers/session.py:20 #: terminal/serializers/session.py:41 terminal/serializers/storage.py:68 msgid "Protocol" msgstr "协议" @@ -1175,28 +1194,12 @@ msgid "Can test asset connectivity" msgstr "可以测试资产连接性" #: assets/models/asset/common.py:284 -msgid "Can push account to asset" -msgstr "可以推送账号到资产" - -#: assets/models/asset/common.py:285 -msgid "Can verify account" -msgstr "可以验证账号" - -#: assets/models/asset/common.py:286 msgid "Can match asset" msgstr "可以匹配资产" -#: assets/models/asset/common.py:287 -msgid "Add asset to node" -msgstr "添加资产到节点" - -#: assets/models/asset/common.py:288 -msgid "Move asset to node" -msgstr "移动资产到节点" - -#: assets/models/asset/common.py:289 -msgid "Remove asset from node" -msgstr "从节点移除资产" +#: assets/models/asset/common.py:285 +msgid "Can change asset nodes" +msgstr "可以修改资产节点" #: assets/models/asset/database.py:10 assets/serializers/asset/common.py:110 #: settings/serializers/email.py:37 @@ -1219,22 +1222,22 @@ msgstr "客户端密钥" 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:114 -#: assets/serializers/platform.py:31 +#: assets/serializers/platform.py:32 msgid "Username selector" msgstr "用户名选择器" #: assets/models/asset/web.py:11 assets/serializers/asset/common.py:115 -#: assets/serializers/platform.py:34 +#: assets/serializers/platform.py:35 msgid "Password selector" msgstr "密码选择器" #: assets/models/asset/web.py:12 assets/serializers/asset/common.py:116 -#: assets/serializers/platform.py:37 +#: assets/serializers/platform.py:38 msgid "Submit selector" msgstr "确认按钮选择器" @@ -1253,7 +1256,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:114 -#: terminal/models/applet/applet.py:150 terminal/models/applet/host.py:107 +#: terminal/models/applet/applet.py:154 terminal/models/applet/host.py:108 #: terminal/models/component/status.py:27 terminal/serializers/applet.py:17 #: terminal/serializers/applet_host.py:93 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1395,45 +1398,45 @@ msgstr "启用" msgid "Ansible config" msgstr "Ansible 配置" -#: assets/models/platform.py:44 assets/serializers/platform.py:60 +#: assets/models/platform.py:44 assets/serializers/platform.py:61 msgid "Ping enabled" msgstr "启用资产探活" -#: assets/models/platform.py:45 assets/serializers/platform.py:61 +#: assets/models/platform.py:45 assets/serializers/platform.py:62 msgid "Ping method" msgstr "资产探活方式" #: assets/models/platform.py:46 assets/models/platform.py:59 -#: assets/serializers/platform.py:62 +#: assets/serializers/platform.py:63 msgid "Gather facts enabled" msgstr "启用收集资产信息" #: assets/models/platform.py:47 assets/models/platform.py:61 -#: assets/serializers/platform.py:63 +#: assets/serializers/platform.py:64 msgid "Gather facts method" msgstr "收集信息方式" -#: assets/models/platform.py:48 assets/serializers/platform.py:66 +#: assets/models/platform.py:48 assets/serializers/platform.py:67 msgid "Change secret enabled" msgstr "启用改密" -#: assets/models/platform.py:50 assets/serializers/platform.py:67 +#: assets/models/platform.py:50 assets/serializers/platform.py:68 msgid "Change secret method" msgstr "改密方式" -#: assets/models/platform.py:52 assets/serializers/platform.py:68 +#: assets/models/platform.py:52 assets/serializers/platform.py:69 msgid "Push account enabled" msgstr "启用账号推送" -#: assets/models/platform.py:54 assets/serializers/platform.py:69 +#: assets/models/platform.py:54 assets/serializers/platform.py:70 msgid "Push account method" msgstr "账号推送方式" -#: assets/models/platform.py:56 assets/serializers/platform.py:64 +#: assets/models/platform.py:56 assets/serializers/platform.py:65 msgid "Verify account enabled" msgstr "开启账号验证" -#: assets/models/platform.py:58 assets/serializers/platform.py:65 +#: assets/models/platform.py:58 assets/serializers/platform.py:66 msgid "Verify account method" msgstr "账号验证方式" @@ -1445,23 +1448,23 @@ msgstr "元数据" msgid "Internal" msgstr "内置" -#: assets/models/platform.py:83 assets/serializers/platform.py:90 +#: assets/models/platform.py:83 assets/serializers/platform.py:94 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:85 assets/serializers/platform.py:118 +#: assets/models/platform.py:85 assets/serializers/platform.py:122 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:87 assets/serializers/platform.py:117 +#: assets/models/platform.py:87 assets/serializers/platform.py:121 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:88 assets/serializers/platform.py:100 +#: assets/models/platform.py:88 assets/serializers/platform.py:104 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:90 assets/serializers/platform.py:97 +#: assets/models/platform.py:90 assets/serializers/platform.py:101 msgid "Automation" msgstr "自动化" @@ -1474,7 +1477,7 @@ msgstr "%(value)s is not an even number" msgid "Auto fill" msgstr "自动代填" -#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:95 +#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:99 #: authentication/serializers/connect_token_secret.py:28 #: authentication/serializers/connect_token_secret.py:66 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:99 @@ -1586,31 +1589,31 @@ 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 根路径" -#: assets/serializers/platform.py:42 +#: assets/serializers/platform.py:43 msgid "Auth with username" msgstr "使用用户名认证" -#: assets/serializers/platform.py:70 +#: assets/serializers/platform.py:71 msgid "Gather accounts enabled" msgstr "启用账号收集" -#: assets/serializers/platform.py:71 +#: assets/serializers/platform.py:72 msgid "Gather accounts method" msgstr "收集账号方式" -#: assets/serializers/platform.py:77 +#: assets/serializers/platform.py:78 msgid "Primary" msgstr "主要的" -#: assets/serializers/platform.py:119 +#: assets/serializers/platform.py:123 msgid "Default Domain" msgstr "默认网域" @@ -1752,7 +1755,7 @@ msgid "Change password" msgstr "改密" #: audits/const.py:34 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:24 terminal/models/component/terminal.py:156 +#: terminal/models/applet/host.py:25 terminal/models/component/terminal.py:156 #: terminal/serializers/session.py:48 msgid "Terminal" msgstr "终端" @@ -1769,7 +1772,7 @@ msgstr "会话日志" msgid "Login log" msgstr "登录日志" -#: audits/const.py:42 terminal/models/applet/host.py:111 +#: audits/const.py:42 terminal/models/applet/host.py:112 #: terminal/models/component/task.py:24 msgid "Task" msgstr "任务" @@ -3551,7 +3554,7 @@ msgstr "组织" msgid "Org name" msgstr "组织名称" -#: orgs/models.py:70 rbac/models/role.py:36 terminal/models/applet/applet.py:32 +#: orgs/models.py:70 rbac/models/role.py:36 terminal/models/applet/applet.py:33 msgid "Builtin" msgstr "内置的" @@ -3795,7 +3798,7 @@ msgstr "权限" msgid "Users amount" msgstr "用户数量" -#: rbac/serializers/role.py:28 terminal/models/applet/applet.py:27 +#: rbac/serializers/role.py:28 terminal/models/applet/applet.py:28 msgid "Display name" msgstr "显示名称" @@ -3859,8 +3862,8 @@ msgstr "任务中心" msgid "My assets" msgstr "我的资产" -#: rbac/tree.py:57 terminal/models/applet/applet.py:42 -#: terminal/models/applet/applet.py:147 terminal/models/applet/host.py:27 +#: rbac/tree.py:57 terminal/models/applet/applet.py:43 +#: terminal/models/applet/applet.py:151 terminal/models/applet/host.py:28 msgid "Applet" msgstr "远程应用" @@ -5349,44 +5352,44 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/applet/applet.py:29 +#: terminal/models/applet/applet.py:30 msgid "Author" msgstr "作者" -#: terminal/models/applet/applet.py:34 +#: terminal/models/applet/applet.py:35 msgid "Tags" msgstr "标签" -#: terminal/models/applet/applet.py:38 terminal/serializers/storage.py:157 +#: terminal/models/applet/applet.py:39 terminal/serializers/storage.py:157 msgid "Hosts" msgstr "主机" -#: terminal/models/applet/applet.py:83 +#: terminal/models/applet/applet.py:84 msgid "Applet pkg not valid, Missing file {}" msgstr "Applet pkg 无效,缺少文件 {}" -#: terminal/models/applet/applet.py:149 terminal/models/applet/host.py:33 -#: terminal/models/applet/host.py:105 +#: terminal/models/applet/applet.py:153 terminal/models/applet/host.py:34 +#: terminal/models/applet/host.py:106 msgid "Hosting" msgstr "宿主机" -#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:43 +#: terminal/models/applet/host.py:19 terminal/serializers/applet_host.py:43 msgid "Deploy options" msgstr "部署参数" -#: terminal/models/applet/host.py:19 +#: terminal/models/applet/host.py:20 msgid "Inited" msgstr "已初始化" -#: terminal/models/applet/host.py:20 +#: terminal/models/applet/host.py:21 msgid "Date inited" msgstr "初始化日期" -#: terminal/models/applet/host.py:21 +#: terminal/models/applet/host.py:22 msgid "Date synced" msgstr "同步日期" -#: terminal/models/applet/host.py:106 +#: terminal/models/applet/host.py:107 msgid "Initial" msgstr "初始化" @@ -5614,7 +5617,7 @@ msgstr "图标" #: terminal/serializers/applet_host.py:24 msgid "Per Session" -msgstr "每会话" +msgstr "每用户" #: terminal/serializers/applet_host.py:25 msgid "Per Device" @@ -7268,6 +7271,18 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Can push account to asset" +#~ msgstr "可以推送账号到资产" + +#~ msgid "Add asset to node" +#~ msgstr "添加资产到节点" + +#~ msgid "Move asset to node" +#~ msgstr "移动资产到节点" + +#~ msgid "Remove asset from node" +#~ msgstr "从节点移除资产" + #~ msgid "Clean audits log" #~ msgstr "清理审计日志" diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index 339039585..de273cfd7 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -101,7 +101,7 @@ class JMSInventory: def asset_to_host(self, asset, account, automation, protocols, platform): host = { - 'name': '{}'.format(asset.name), + 'name': '{}'.format(asset.name.replace(' ', '_')), 'jms_asset': { 'id': str(asset.id), 'name': asset.name, 'address': asset.address, 'type': asset.type, 'category': asset.category, diff --git a/apps/ops/api/playbook.py b/apps/ops/api/playbook.py index ea856df37..c26643fe5 100644 --- a/apps/ops/api/playbook.py +++ b/apps/ops/api/playbook.py @@ -145,16 +145,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/ops/signal_handlers.py b/apps/ops/signal_handlers.py index b27a741ae..5e5090e4b 100644 --- a/apps/ops/signal_handlers.py +++ b/apps/ops/signal_handlers.py @@ -46,9 +46,25 @@ def sync_registered_tasks(*args, **kwargs): @receiver(django_ready) def check_registered_tasks(*args, **kwargs): attrs = ['verbose_name', 'activity_callback'] + ignores = [ + 'users.tasks.check_user_expired_periodic', 'ops.tasks.clean_celery_periodic_tasks', + 'terminal.tasks.delete_terminal_status_period', 'ops.tasks.check_server_performance_period', + 'settings.tasks.ldap.import_ldap_user', 'users.tasks.check_password_expired', + 'assets.tasks.nodes_amount.check_node_assets_amount_task', 'notifications.notifications.publish_task', + 'perms.tasks.check_asset_permission_will_expired', + 'ops.tasks.create_or_update_registered_periodic_tasks', 'perms.tasks.check_asset_permission_expired', + 'settings.tasks.ldap.import_ldap_user_periodic', 'users.tasks.check_password_expired_periodic', + 'common.utils.verify_code.send_async', 'assets.tasks.nodes_amount.check_node_assets_amount_period_task', + 'users.tasks.check_user_expired', 'orgs.tasks.refresh_org_cache_task', + 'terminal.tasks.upload_session_replay_to_external_storage', 'terminal.tasks.clean_orphan_session', + 'audits.tasks.clean_audits_log_period', 'authentication.tasks.clean_django_sessions' + ] + for name, task in app.tasks.items(): if name.startswith('celery.'): continue + if name in ignores: + continue for attr in attrs: if not hasattr(task, attr): # print('>>> Task {} has no attribute {}'.format(name, attr)) 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/rbac/const.py b/apps/rbac/const.py index cb9fa96c6..1c0f594f9 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -49,6 +49,10 @@ exclude_permissions = ( ('assets', 'gatherfactsautomation', '*', '*'), ('assets', 'commandfilter', '*', '*'), ('assets', 'commandfilterrule', '*', '*'), + ('assets', 'asset', 'add,move', 'assettonode'), + ('assets', 'asset', 'remove', 'assetfromnode'), + ('assets', 'asset', 'test', 'account'), + ('assets', 'asset', 'push', 'assetaccount'), ('accounts', 'historicalaccount', '*', '*'), ('accounts', 'accountbaseautomation', '*', '*'), diff --git a/apps/rbac/migrations/0011_remove_redundant_permission.py b/apps/rbac/migrations/0011_remove_redundant_permission.py index d84e86ab3..6febd432c 100644 --- a/apps/rbac/migrations/0011_remove_redundant_permission.py +++ b/apps/rbac/migrations/0011_remove_redundant_permission.py @@ -18,7 +18,8 @@ def migrate_remove_redundant_permission(apps, *args): model.objects.filter(app_label='assets', model__in=[ 'authbook', 'historicalauthbook', 'test_gateway', - 'accountbackupplan', 'accountbackupplanexecution', 'gathereduser', 'systemuser' + 'accountbackupplan', 'accountbackupplanexecution', + 'gathereduser', 'systemuser' ]).delete() model.objects.filter(app_label='perms', model__in=[ @@ -27,7 +28,8 @@ def migrate_remove_redundant_permission(apps, *args): perm_model = apps.get_model('auth', 'Permission') perm_model.objects.filter(codename__in=[ - 'view_permusergroupasset', 'view_permuserasset', 'push_assetsystemuser', 'change_accountsecret' + 'view_permusergroupasset', 'view_permuserasset', 'push_assetsystemuser', + 'add_assettonode', 'move_assettonode', 'remove_assetfromnode', ]).delete() 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"))