From 15acfe84b0018c784de791b39dc371dc008b2cca Mon Sep 17 00:00:00 2001
From: fit2bot <68588906+fit2bot@users.noreply.github.com>
Date: Thu, 21 Mar 2024 11:05:04 +0800
Subject: [PATCH] =?UTF-8?q?perf:=20=E6=94=B9=E5=AF=86=E8=AE=B0=E5=BD=95?=
=?UTF-8?q?=E5=8F=AF=E6=9F=A5=E7=9C=8B=E5=AF=86=E6=96=87=20(#12821)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* perf: 改密记录可查看密文
* perf: 自动化任务错误处理
* feat: 改密记录可批量重试 新增更多过滤选项
* perf: 改密任务失败添加消息通知
---------
Co-authored-by: feng <1304903146@qq.com>
---
.../accounts/api/automations/change_secret.py | 38 ++++++++---
.../automations/backup_account/handlers.py | 2 +-
.../automations/change_secret/manager.py | 64 +++++++++++++++----
.../automations/gather_accounts/manager.py | 2 +-
.../automations/remove_account/manager.py | 13 ++--
.../automations/verify_account/manager.py | 10 ++-
apps/accounts/const/automation.py | 8 ++-
apps/accounts/filters.py | 11 +++-
.../models/automations/backup_account.py | 2 +-
.../models/automations/change_secret.py | 7 +-
apps/accounts/notifications.py | 31 +++++++++
.../serializers/automations/change_secret.py | 22 +++++--
apps/accounts/tasks/automation.py | 36 ++++++-----
.../accounts/asset_account_change_info.html | 4 +-
.../accounts/change_secret_failed_info.html | 36 +++++++++++
apps/assets/automations/ping/manager.py | 24 ++++---
.../automations/ping_gateway/manager.py | 24 ++++---
17 files changed, 262 insertions(+), 72 deletions(-)
create mode 100644 apps/accounts/templates/accounts/change_secret_failed_info.html
diff --git a/apps/accounts/api/automations/change_secret.py b/apps/accounts/api/automations/change_secret.py
index 32fea5d16..05ee515dc 100644
--- a/apps/accounts/api/automations/change_secret.py
+++ b/apps/accounts/api/automations/change_secret.py
@@ -6,9 +6,12 @@ from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
+from accounts.filters import ChangeSecretRecordFilterSet
from accounts.models import ChangeSecretAutomation, ChangeSecretRecord
from accounts.tasks import execute_automation_record_task
+from authentication.permissions import UserConfirmation, ConfirmType
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
+from rbac.permissions import RBACPermission
from .base import (
AutomationAssetsListApi, AutomationRemoveAssetApi, AutomationAddAssetApi,
AutomationNodeAddRemoveApi, AutomationExecutionViewSet
@@ -30,29 +33,48 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet):
class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
- serializer_class = serializers.ChangeSecretRecordSerializer
- filterset_fields = ('asset_id', 'execution_id')
+ filterset_class = ChangeSecretRecordFilterSet
search_fields = ('asset__address',)
tp = AutomationTypes.change_secret
+ serializer_classes = {
+ 'default': serializers.ChangeSecretRecordSerializer,
+ 'secret': serializers.ChangeSecretRecordViewSecretSerializer,
+ }
rbac_perms = {
'execute': 'accounts.add_changesecretexecution',
+ 'secret': 'accounts.view_changesecretrecord',
}
+ def get_permissions(self):
+ if self.action == 'secret':
+ self.permission_classes = [
+ RBACPermission,
+ UserConfirmation.require(ConfirmType.MFA)
+ ]
+ return super().get_permissions()
+
def get_queryset(self):
return ChangeSecretRecord.objects.all()
@action(methods=['post'], detail=False, url_path='execute')
def execute(self, request, *args, **kwargs):
- record_id = request.data.get('record_id')
- record = self.get_queryset().filter(pk=record_id)
- if not record:
+ record_ids = request.data.get('record_ids')
+ records = self.get_queryset().filter(id__in=record_ids)
+ execution_count = records.values_list('execution_id', flat=True).distinct().count()
+ if execution_count != 1:
return Response(
- {'detail': 'record not found'},
- status=status.HTTP_404_NOT_FOUND
+ {'detail': 'Only one execution is allowed to execute'},
+ status=status.HTTP_400_BAD_REQUEST
)
- task = execute_automation_record_task.delay(record_id, self.tp)
+ task = execute_automation_record_task.delay(record_ids, self.tp)
return Response({'task': task.id}, status=status.HTTP_200_OK)
+ @action(methods=['get'], detail=True, url_path='secret')
+ def secret(self, request, *args, **kwargs):
+ instance = self.get_object()
+ serializer = self.get_serializer(instance)
+ return Response(serializer.data)
+
class ChangSecretExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
diff --git a/apps/accounts/automations/backup_account/handlers.py b/apps/accounts/automations/backup_account/handlers.py
index 6a00a2436..e2a0cdb3c 100644
--- a/apps/accounts/automations/backup_account/handlers.py
+++ b/apps/accounts/automations/backup_account/handlers.py
@@ -6,7 +6,7 @@ from django.conf import settings
from rest_framework import serializers
from xlsxwriter import Workbook
-from accounts.const.automation import AccountBackupType
+from accounts.const import AccountBackupType
from accounts.models.automations.backup_account import AccountBackupAutomation
from accounts.notifications import AccountBackupExecutionTaskMsg, AccountBackupByObjStorageExecutionTaskMsg
from accounts.serializers import AccountSecretSerializer
diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py
index e4d1a0fd5..160988748 100644
--- a/apps/accounts/automations/change_secret/manager.py
+++ b/apps/accounts/automations/change_secret/manager.py
@@ -7,9 +7,9 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from xlsxwriter import Workbook
-from accounts.const import AutomationTypes, SecretType, SSHKeyStrategy, SecretStrategy
+from accounts.const import AutomationTypes, SecretType, SSHKeyStrategy, SecretStrategy, ChangeSecretRecordStatusChoice
from accounts.models import ChangeSecretRecord
-from accounts.notifications import ChangeSecretExecutionTaskMsg
+from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretFailedMsg
from accounts.serializers import ChangeSecretRecordBackUpSerializer
from assets.const import HostTypes
from common.utils import get_logger
@@ -27,7 +27,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.record_id = self.execution.snapshot.get('record_id')
+ self.record_map = self.execution.snapshot.get('record_map', {})
self.secret_type = self.execution.snapshot.get('secret_type')
self.secret_strategy = self.execution.snapshot.get(
'secret_strategy', SecretStrategy.custom
@@ -123,14 +123,20 @@ class ChangeSecretManager(AccountBasePlaybookManager):
print(f'new_secret is None, account: {account}')
continue
- if self.record_id is None:
+ asset_account_id = f'{asset.id}-{account.id}'
+ if asset_account_id not in self.record_map:
recorder = ChangeSecretRecord(
asset=asset, account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret,
)
records.append(recorder)
else:
- recorder = ChangeSecretRecord.objects.get(id=self.record_id)
+ record_id = self.record_map[asset_account_id]
+ try:
+ recorder = ChangeSecretRecord.objects.get(id=record_id)
+ except ChangeSecretRecord.DoesNotExist:
+ print(f"Record {record_id} not found")
+ continue
self.name_recorder_mapper[h['name']] = recorder
@@ -158,25 +164,43 @@ class ChangeSecretManager(AccountBasePlaybookManager):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
- recorder.status = 'success'
+ recorder.status = ChangeSecretRecordStatusChoice.success.value
recorder.date_finished = timezone.now()
- recorder.save()
+
account = recorder.account
if not account:
print("Account not found, deleted ?")
return
account.secret = recorder.new_secret
account.date_updated = timezone.now()
- account.save(update_fields=['secret', 'date_updated'])
+
+ max_retries = 3
+ retry_count = 0
+
+ while retry_count < max_retries:
+ try:
+ recorder.save()
+ account.save(update_fields=['secret', 'date_updated'])
+ break
+ except Exception as e:
+ retry_count += 1
+ if retry_count == max_retries:
+ self.on_host_error(host, str(e), result)
+ else:
+ print(f'retry {retry_count} times for {host} recorder save error: {e}')
+ time.sleep(1)
def on_host_error(self, host, error, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
- recorder.status = 'failed'
+ recorder.status = ChangeSecretRecordStatusChoice.failed.value
recorder.date_finished = timezone.now()
recorder.error = error
- recorder.save()
+ try:
+ recorder.save()
+ except Exception as e:
+ print(f"\033[31m Save {host} recorder error: {e} \033[0m\n")
def on_runner_failed(self, runner, e):
logger.error("Account error: ", e)
@@ -192,7 +216,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
def get_summary(recorders):
total, succeed, failed = 0, 0, 0
for recorder in recorders:
- if recorder.status == 'success':
+ if recorder.status == ChangeSecretRecordStatusChoice.success.value:
succeed += 1
else:
failed += 1
@@ -209,9 +233,25 @@ class ChangeSecretManager(AccountBasePlaybookManager):
summary = self.get_summary(recorders)
print(summary, end='')
- if self.record_id:
+ if self.record_map:
return
+ failed_recorders = [
+ r for r in recorders
+ if r.status == ChangeSecretRecordStatusChoice.failed.value
+ ]
+ super_users = User.get_super_admins()
+
+ if failed_recorders and super_users:
+ name = self.execution.snapshot.get('name')
+ execution_id = str(self.execution.id)
+ _ids = [r.id for r in failed_recorders]
+ asset_account_errors = ChangeSecretRecord.objects.filter(
+ id__in=_ids).values_list('asset__name', 'account__username', 'error')
+
+ for user in super_users:
+ ChangeSecretFailedMsg(name, execution_id, user, asset_account_errors).publish()
+
self.send_recorder_mail(recorders, summary)
def send_recorder_mail(self, recorders, summary):
diff --git a/apps/accounts/automations/gather_accounts/manager.py b/apps/accounts/automations/gather_accounts/manager.py
index 1c9ae990f..f8949df4d 100644
--- a/apps/accounts/automations/gather_accounts/manager.py
+++ b/apps/accounts/automations/gather_accounts/manager.py
@@ -58,7 +58,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
result = self.filter_success_result(asset.type, info)
self.collect_asset_account_info(asset, result)
else:
- logger.error(f'Not found {host} info')
+ print(f'\033[31m Not found {host} info \033[0m\n')
def update_or_create_accounts(self):
for asset, data in self.asset_account_info.items():
diff --git a/apps/accounts/automations/remove_account/manager.py b/apps/accounts/automations/remove_account/manager.py
index 37dd28f2d..38a22538b 100644
--- a/apps/accounts/automations/remove_account/manager.py
+++ b/apps/accounts/automations/remove_account/manager.py
@@ -60,8 +60,11 @@ class RemoveAccountManager(AccountBasePlaybookManager):
if not tuple_asset_gather_account:
return
asset, gather_account = tuple_asset_gather_account
- Account.objects.filter(
- asset_id=asset.id,
- username=gather_account.username
- ).delete()
- gather_account.delete()
+ try:
+ Account.objects.filter(
+ asset_id=asset.id,
+ username=gather_account.username
+ ).delete()
+ gather_account.delete()
+ except Exception as e:
+ print(f'\033[31m Delete account {gather_account.username} failed: {e} \033[0m\n')
diff --git a/apps/accounts/automations/verify_account/manager.py b/apps/accounts/automations/verify_account/manager.py
index 94be53b89..235e5c601 100644
--- a/apps/accounts/automations/verify_account/manager.py
+++ b/apps/accounts/automations/verify_account/manager.py
@@ -76,8 +76,14 @@ class VerifyAccountManager(AccountBasePlaybookManager):
def on_host_success(self, host, result):
account = self.host_account_mapper.get(host)
- account.set_connectivity(Connectivity.OK)
+ try:
+ account.set_connectivity(Connectivity.OK)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} connectivity failed: {e} \033[0m\n')
def on_host_error(self, host, error, result):
account = self.host_account_mapper.get(host)
- account.set_connectivity(Connectivity.ERR)
+ try:
+ account.set_connectivity(Connectivity.ERR)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} connectivity failed: {e} \033[0m\n')
diff --git a/apps/accounts/const/automation.py b/apps/accounts/const/automation.py
index cde7fe982..c0419486d 100644
--- a/apps/accounts/const/automation.py
+++ b/apps/accounts/const/automation.py
@@ -16,7 +16,7 @@ DEFAULT_PASSWORD_RULES = {
__all__ = [
'AutomationTypes', 'SecretStrategy', 'SSHKeyStrategy', 'Connectivity',
'DEFAULT_PASSWORD_LENGTH', 'DEFAULT_PASSWORD_RULES', 'TriggerChoice',
- 'PushAccountActionChoice', 'AccountBackupType'
+ 'PushAccountActionChoice', 'AccountBackupType', 'ChangeSecretRecordStatusChoice',
]
@@ -103,3 +103,9 @@ class AccountBackupType(models.TextChoices):
email = 'email', _('Email')
# 目前只支持sftp方式
object_storage = 'object_storage', _('SFTP')
+
+
+class ChangeSecretRecordStatusChoice(models.TextChoices):
+ failed = 'failed', _('Failed')
+ success = 'success', _('Success')
+ pending = 'pending', _('Pending')
diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py
index b26e9d391..bffab9956 100644
--- a/apps/accounts/filters.py
+++ b/apps/accounts/filters.py
@@ -5,7 +5,7 @@ from django_filters import rest_framework as drf_filters
from assets.models import Node
from common.drf.filters import BaseFilterSet
-from .models import Account, GatheredAccount
+from .models import Account, GatheredAccount, ChangeSecretRecord
class AccountFilterSet(BaseFilterSet):
@@ -61,3 +61,12 @@ class GatheredAccountFilterSet(BaseFilterSet):
class Meta:
model = GatheredAccount
fields = ['id', 'username']
+
+
+class ChangeSecretRecordFilterSet(BaseFilterSet):
+ asset_name = drf_filters.CharFilter(field_name='asset__name', lookup_expr='icontains')
+ account_username = drf_filters.CharFilter(field_name='account__username', lookup_expr='icontains')
+
+ class Meta:
+ model = ChangeSecretRecord
+ fields = ['id', 'status', 'asset_id', 'execution_id']
diff --git a/apps/accounts/models/automations/backup_account.py b/apps/accounts/models/automations/backup_account.py
index db325c702..177e0dfa1 100644
--- a/apps/accounts/models/automations/backup_account.py
+++ b/apps/accounts/models/automations/backup_account.py
@@ -8,7 +8,7 @@ from django.db import models
from django.db.models import F
from django.utils.translation import gettext_lazy as _
-from accounts.const.automation import AccountBackupType
+from accounts.const import AccountBackupType
from common.const.choices import Trigger
from common.db import fields
from common.db.encoder import ModelJSONFieldEncoder
diff --git a/apps/accounts/models/automations/change_secret.py b/apps/accounts/models/automations/change_secret.py
index c575ac161..48c0a45e1 100644
--- a/apps/accounts/models/automations/change_secret.py
+++ b/apps/accounts/models/automations/change_secret.py
@@ -2,7 +2,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from accounts.const import (
- AutomationTypes
+ AutomationTypes, ChangeSecretRecordStatusChoice
)
from common.db import fields
from common.db.models import JMSBaseModel
@@ -40,7 +40,10 @@ class ChangeSecretRecord(JMSBaseModel):
new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret'))
date_started = models.DateTimeField(blank=True, null=True, verbose_name=_('Date started'))
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('Date finished'))
- status = models.CharField(max_length=16, default='pending', verbose_name=_('Status'))
+ status = models.CharField(
+ max_length=16, verbose_name=_('Status'),
+ default=ChangeSecretRecordStatusChoice.pending.value
+ )
error = models.TextField(blank=True, null=True, verbose_name=_('Error'))
class Meta:
diff --git a/apps/accounts/notifications.py b/apps/accounts/notifications.py
index 0082f8b80..713f44cd6 100644
--- a/apps/accounts/notifications.py
+++ b/apps/accounts/notifications.py
@@ -1,6 +1,7 @@
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
+from accounts.models import ChangeSecretRecord
from common.tasks import send_mail_attachment_async, upload_backup_to_obj_storage
from notifications.notifications import UserMessage
from terminal.models.component.storage import ReplayStorage
@@ -98,3 +99,33 @@ class GatherAccountChangeMsg(UserMessage):
def gen_test_msg(cls):
user = User.objects.first()
return cls(user, {})
+
+
+class ChangeSecretFailedMsg(UserMessage):
+ subject = _('Change secret or push account failed information')
+
+ def __init__(self, name, execution_id, user, asset_account_errors: list):
+ self.name = name
+ self.execution_id = execution_id
+ self.asset_account_errors = asset_account_errors
+ super().__init__(user)
+
+ def get_html_msg(self) -> dict:
+ context = {
+ 'name': self.name,
+ 'recipient': self.user,
+ 'execution_id': self.execution_id,
+ 'asset_account_errors': self.asset_account_errors
+ }
+ message = render_to_string('accounts/change_secret_failed_info.html', context)
+
+ return {
+ 'subject': str(self.subject),
+ 'message': message
+ }
+
+ @classmethod
+ def gen_test_msg(cls):
+ user = User.objects.first()
+ record = ChangeSecretRecord.objects.first()
+ return cls(user, [record])
diff --git a/apps/accounts/serializers/automations/change_secret.py b/apps/accounts/serializers/automations/change_secret.py
index 0ef0c4b53..e3e9e2ce0 100644
--- a/apps/accounts/serializers/automations/change_secret.py
+++ b/apps/accounts/serializers/automations/change_secret.py
@@ -4,7 +4,8 @@ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from accounts.const import (
- AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy
+ AutomationTypes, SecretType, SecretStrategy,
+ SSHKeyStrategy, ChangeSecretRecordStatusChoice
)
from accounts.models import (
Account, ChangeSecretAutomation,
@@ -21,6 +22,7 @@ logger = get_logger(__file__)
__all__ = [
'ChangeSecretAutomationSerializer',
'ChangeSecretRecordSerializer',
+ 'ChangeSecretRecordViewSecretSerializer',
'ChangeSecretRecordBackUpSerializer',
'ChangeSecretUpdateAssetSerializer',
'ChangeSecretUpdateNodeSerializer',
@@ -104,7 +106,10 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
class ChangeSecretRecordSerializer(serializers.ModelSerializer):
is_success = serializers.SerializerMethodField(label=_('Is success'))
asset = ObjectRelatedField(queryset=Asset.objects, label=_('Asset'))
- account = ObjectRelatedField(queryset=Account.objects, label=_('Account'))
+ account = ObjectRelatedField(
+ queryset=Account.objects, label=_('Account'),
+ attrs=("id", "name", "username")
+ )
execution = ObjectRelatedField(
queryset=AutomationExecution.objects, label=_('Automation task execution')
)
@@ -119,7 +124,16 @@ class ChangeSecretRecordSerializer(serializers.ModelSerializer):
@staticmethod
def get_is_success(obj):
- return obj.status == 'success'
+ return obj.status == ChangeSecretRecordStatusChoice.success.value
+
+
+class ChangeSecretRecordViewSecretSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ChangeSecretRecord
+ fields = [
+ 'id', 'old_secret', 'new_secret',
+ ]
+ read_only_fields = fields
class ChangeSecretRecordBackUpSerializer(serializers.ModelSerializer):
@@ -145,7 +159,7 @@ class ChangeSecretRecordBackUpSerializer(serializers.ModelSerializer):
@staticmethod
def get_is_success(obj):
- if obj.status == 'success':
+ if obj.status == ChangeSecretRecordStatusChoice.success.value:
return _("Success")
return _("Failed")
diff --git a/apps/accounts/tasks/automation.py b/apps/accounts/tasks/automation.py
index 7c81e417f..f691825ef 100644
--- a/apps/accounts/tasks/automation.py
+++ b/apps/accounts/tasks/automation.py
@@ -36,14 +36,14 @@ def execute_account_automation_task(pid, trigger, tp):
instance.execute(trigger)
-def record_task_activity_callback(self, record_id, *args, **kwargs):
+def record_task_activity_callback(self, record_ids, *args, **kwargs):
from accounts.models import ChangeSecretRecord
with tmp_to_root_org():
- record = get_object_or_none(ChangeSecretRecord, id=record_id)
- if not record:
+ records = ChangeSecretRecord.objects.filter(id__in=record_ids)
+ if not records:
return
- resource_ids = [record.id]
- org_id = record.execution.org_id
+ resource_ids = [str(i.id) for i in records]
+ org_id = records[0].execution.org_id
return resource_ids, org_id
@@ -51,22 +51,26 @@ def record_task_activity_callback(self, record_id, *args, **kwargs):
queue='ansible', verbose_name=_('Execute automation record'),
activity_callback=record_task_activity_callback
)
-def execute_automation_record_task(record_id, tp):
+def execute_automation_record_task(record_ids, tp):
from accounts.models import ChangeSecretRecord
+ task_name = gettext_noop('Execute automation record')
+
with tmp_to_root_org():
- instance = get_object_or_none(ChangeSecretRecord, pk=record_id)
- if not instance:
- logger.error("No automation record found: {}".format(record_id))
+ records = ChangeSecretRecord.objects.filter(id__in=record_ids)
+
+ if not records:
+ logger.error('No automation record found: {}'.format(record_ids))
return
- task_name = gettext_noop('Execute automation record')
+ record = records[0]
+ record_map = {f'{record.asset_id}-{record.account_id}': str(record.id) for record in records}
task_snapshot = {
- 'secret': instance.new_secret,
- 'secret_type': instance.execution.snapshot.get('secret_type'),
- 'accounts': [str(instance.account_id)],
- 'assets': [str(instance.asset_id)],
'params': {},
- 'record_id': record_id,
+ 'record_map': record_map,
+ 'secret': record.new_secret,
+ 'secret_type': record.execution.snapshot.get('secret_type'),
+ 'assets': [str(instance.asset_id) for instance in records],
+ 'accounts': [str(instance.account_id) for instance in records],
}
- with tmp_to_org(instance.execution.org_id):
+ with tmp_to_org(record.execution.org_id):
quickstart_automation_by_snapshot(task_name, tp, task_snapshot)
diff --git a/apps/accounts/templates/accounts/asset_account_change_info.html b/apps/accounts/templates/accounts/asset_account_change_info.html
index 242908921..b778b3cd3 100644
--- a/apps/accounts/templates/accounts/asset_account_change_info.html
+++ b/apps/accounts/templates/accounts/asset_account_change_info.html
@@ -1,10 +1,10 @@
{% load i18n %}
-
+
{% trans 'Gather account change information' %}
- {% trans 'Asset' %} |
+ {% trans 'Asset' %} |
{% trans 'Added account' %} |
{% trans 'Deleted account' %} |
diff --git a/apps/accounts/templates/accounts/change_secret_failed_info.html b/apps/accounts/templates/accounts/change_secret_failed_info.html
new file mode 100644
index 000000000..442ef44b4
--- /dev/null
+++ b/apps/accounts/templates/accounts/change_secret_failed_info.html
@@ -0,0 +1,36 @@
+{% load i18n %}
+
+{% trans 'Task name' %}: {{ name }}
+{% trans 'Task execution id' %}: {{ execution_id }}
+{% trans 'Respectful' %} {{ recipient }}
+{% trans 'Hello! The following is the failure of changing the password of your assets or pushing the account. Please check and handle it in time.' %}
+
+
+
+
+ {% trans 'Asset' %} |
+ {% trans 'Account' %} |
+ {% trans 'Error' %} |
+
+
+
+ {% for asset_name, account_username, error in asset_account_errors %}
+
+ {{ asset_name }} |
+ {{ account_username }} |
+
+
+ {{ error }}
+
+ |
+
+ {% endfor %}
+
+
\ No newline at end of file
diff --git a/apps/assets/automations/ping/manager.py b/apps/assets/automations/ping/manager.py
index 9925ad48d..6249a40c7 100644
--- a/apps/assets/automations/ping/manager.py
+++ b/apps/assets/automations/ping/manager.py
@@ -25,14 +25,22 @@ class PingManager(BasePlaybookManager):
def on_host_success(self, host, result):
asset, account = self.host_asset_and_account_mapper.get(host)
- asset.set_connectivity(Connectivity.OK)
- if not account:
- return
- account.set_connectivity(Connectivity.OK)
+ try:
+ asset.set_connectivity(Connectivity.OK)
+ if not account:
+ return
+ account.set_connectivity(Connectivity.OK)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} or '
+ f'update asset {asset.name} connectivity failed: {e} \033[0m\n')
def on_host_error(self, host, error, result):
asset, account = self.host_asset_and_account_mapper.get(host)
- asset.set_connectivity(Connectivity.ERR)
- if not account:
- return
- account.set_connectivity(Connectivity.ERR)
+ try:
+ asset.set_connectivity(Connectivity.ERR)
+ if not account:
+ return
+ account.set_connectivity(Connectivity.ERR)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} or '
+ f'update asset {asset.name} connectivity failed: {e} \033[0m\n')
diff --git a/apps/assets/automations/ping_gateway/manager.py b/apps/assets/automations/ping_gateway/manager.py
index c272570d3..f42090f07 100644
--- a/apps/assets/automations/ping_gateway/manager.py
+++ b/apps/assets/automations/ping_gateway/manager.py
@@ -92,18 +92,26 @@ class PingGatewayManager:
@staticmethod
def on_host_success(gateway, account):
print('\033[32m {} -> {}\033[0m\n'.format(gateway, account))
- gateway.set_connectivity(Connectivity.OK)
- if not account:
- return
- account.set_connectivity(Connectivity.OK)
+ try:
+ gateway.set_connectivity(Connectivity.OK)
+ if not account:
+ return
+ account.set_connectivity(Connectivity.OK)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} or '
+ f'update asset {gateway.name} connectivity failed: {e} \033[0m\n')
@staticmethod
def on_host_error(gateway, account, error):
print('\033[31m {} -> {} 原因: {} \033[0m\n'.format(gateway, account, error))
- gateway.set_connectivity(Connectivity.ERR)
- if not account:
- return
- account.set_connectivity(Connectivity.ERR)
+ try:
+ gateway.set_connectivity(Connectivity.ERR)
+ if not account:
+ return
+ account.set_connectivity(Connectivity.ERR)
+ except Exception as e:
+ print(f'\033[31m Update account {account.name} or '
+ f'update asset {gateway.name} connectivity failed: {e} \033[0m\n')
@staticmethod
def before_runner_start():