perf: Change secret report

pull/14575/head
feng 2024-12-02 19:09:16 +08:00 committed by feng626
parent 1dabaf04f0
commit eb901b2946
6 changed files with 332 additions and 188 deletions

View File

@ -125,6 +125,7 @@ class ChangSecretExecutionViewSet(AutomationExecutionViewSet):
("list", "accounts.view_changesecretexecution"), ("list", "accounts.view_changesecretexecution"),
("retrieve", "accounts.view_changesecretexecution"), ("retrieve", "accounts.view_changesecretexecution"),
("create", "accounts.add_changesecretexecution"), ("create", "accounts.add_changesecretexecution"),
("report", "accounts.view_changesecretexecution"),
) )
tp = AutomationTypes.change_secret tp = AutomationTypes.change_secret

View File

@ -11,7 +11,7 @@ from accounts.const import (
AutomationTypes, SecretType, SSHKeyStrategy, SecretStrategy, ChangeSecretRecordStatusChoice AutomationTypes, SecretType, SSHKeyStrategy, SecretStrategy, ChangeSecretRecordStatusChoice
) )
from accounts.models import ChangeSecretRecord, BaseAccountQuerySet from accounts.models import ChangeSecretRecord, BaseAccountQuerySet
from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretFailedMsg from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretReportMsg
from accounts.serializers import ChangeSecretRecordBackUpSerializer from accounts.serializers import ChangeSecretRecordBackUpSerializer
from assets.const import HostTypes from assets.const import HostTypes
from common.db.utils import safe_db_connection from common.db.utils import safe_db_connection
@ -183,6 +183,14 @@ class ChangeSecretManager(AccountBasePlaybookManager):
with safe_db_connection(): with safe_db_connection():
recorder.save(update_fields=['status', 'date_finished']) recorder.save(update_fields=['status', 'date_finished'])
account.save(update_fields=['secret', 'version', 'date_updated']) account.save(update_fields=['secret', 'version', 'date_updated'])
self.summary['ok_accounts'] += 1
self.result['ok_accounts'].append(
{
"asset": str(account.asset),
"username": account.username,
}
)
super().on_host_success(host, result)
def on_host_error(self, host, error, result): def on_host_error(self, host, error, result):
recorder = self.name_recorder_mapper.get(host) recorder = self.name_recorder_mapper.get(host)
@ -195,9 +203,14 @@ class ChangeSecretManager(AccountBasePlaybookManager):
recorder.save() recorder.save()
except Exception as e: except Exception as e:
print(f"\033[31m Save {host} recorder error: {e} \033[0m\n") print(f"\033[31m Save {host} recorder error: {e} \033[0m\n")
self.summary['fail_accounts'] += 1
def on_runner_failed(self, runner, e, **kwargs): self.result['fail_accounts'].append(
logger.error("Account error: ", e) {
"asset": str(recorder.asset),
"username": recorder.account.username,
}
)
super().on_host_success(host, result)
def check_secret(self): def check_secret(self):
if self.secret_strategy == SecretStrategy.custom \ if self.secret_strategy == SecretStrategy.custom \
@ -236,24 +249,13 @@ class ChangeSecretManager(AccountBasePlaybookManager):
if self.record_map: if self.record_map:
return return
failed_recorders = [
r for r in recorders
if r.status == ChangeSecretRecordStatusChoice.failed.value
]
recipients = self.execution.recipients recipients = self.execution.recipients
if not recipients: if not recipients:
return return
if failed_recorders: context = self.get_report_context()
name = self.execution.snapshot.get('name') for user in recipients:
execution_id = str(self.execution.id) ChangeSecretReportMsg(user, context).publish()
_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 recipients:
ChangeSecretFailedMsg(name, execution_id, user, asset_account_errors).publish()
if not recorders: if not recorders:
return return
@ -295,3 +297,6 @@ class ChangeSecretManager(AccountBasePlaybookManager):
ws.write_string(row_index, col_index, col_data) ws.write_string(row_index, col_index, col_data)
wb.close() wb.close()
return True return True
def get_report_template(self):
return "accounts/change_secret_report.html"

View File

@ -1,5 +1,6 @@
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from premailer import transform
from accounts.models import ChangeSecretRecord from accounts.models import ChangeSecretRecord
from common.tasks import send_mail_attachment_async, upload_backup_to_obj_storage from common.tasks import send_mail_attachment_async, upload_backup_to_obj_storage
@ -101,24 +102,19 @@ class GatherAccountChangeMsg(UserMessage):
return cls(user, {}) return cls(user, {})
class ChangeSecretFailedMsg(UserMessage): class ChangeSecretReportMsg(UserMessage):
subject = _('Change secret or push account failed information') subject = _('Change secret or push account failed information')
def __init__(self, name, execution_id, user, asset_account_errors: list): def __init__(self, user, context: dict):
self.name = name self.context = context
self.execution_id = execution_id
self.asset_account_errors = asset_account_errors
super().__init__(user) super().__init__(user)
def get_html_msg(self) -> dict: def get_html_msg(self) -> dict:
context = { report = render_to_string(
'name': self.name, 'accounts/change_secret_report.html',
'recipient': self.user, self.context
'execution_id': self.execution_id, )
'asset_account_errors': self.asset_account_errors message = transform(report)
}
message = render_to_string('accounts/change_secret_failed_info.html', context)
return { return {
'subject': str(self.subject), 'subject': str(self.subject),
'message': message 'message': message
@ -130,4 +126,4 @@ class ChangeSecretFailedMsg(UserMessage):
user = User.objects.first() user = User.objects.first()
record = ChangeSecretRecord.objects.first() record = ChangeSecretRecord.objects.first()
execution_id = str(record.execution_id) execution_id = str(record.execution_id)
return cls(name, execution_id, user, []) return cls(user, {})

View File

@ -0,0 +1,134 @@
{% load i18n %}
<div class='summary'>
<p>{% trans 'The following is a summary of account change secret tasks, please review and handle them' %}</p>
<table>
<caption></caption>
<thead>
<tr>
<th colspan='2'>任务汇总:</th>
</tr>
</thead>
<tbody>
<tr>
<td>{% trans 'Task name' %}:</td>
<td>{{ execution.automation.name }} </td>
</tr>
<tr>
<td>{% trans 'Date start' %}:</td>
<td>{{ execution.date_start | date:"Y/m/d H:i:s" }}</td>
</tr>
<tr>
<td>{% trans 'Date end' %}:</td>
<td>{{ execution.date_finished | date:"Y/m/d H:i:s" }}</td>
</tr>
<tr>
<td>{% trans 'Time using' %}:</td>
<td>{{ execution.duration }}s</td>
</tr>
<tr>
<td>{% trans 'Assets count' %}:</td>
<td>{{ summary.total_assets }}</td>
</tr>
<tr>
<td>{% trans 'Asset success count' %}:</td>
<td>{{ summary.ok_assets }}</td>
</tr>
<tr>
<td>{% trans 'Asset failed count' %}:</td>
<td>{{ summary.fail_assets }}</td>
</tr>
<tr>
<td>{% trans 'Asset not support count' %}:</td>
<td>{{ summary.error_assets }}</td>
</tr>
</tbody>
</table>
</div>
<div class='result'>
{% if summary.ok_accounts %}
<p>{% trans 'Success accounts' %}: {{ summary.ok_accounts }}</p>
<table>
<caption></caption>
<thead>
<tr>
<th>{% trans 'No.' %}</th>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Username' %}</th>
</tr>
</thead>
<tbody>
{% for account in result.ok_accounts %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ account.asset }}</td>
<td>{{ account.username }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<div class='result'>
{% if summary.lost_accounts %}
<p>{% trans 'Failed accounts' %}: {{ summary.fail_accounts }}</p>
<table>
<caption></caption>
<thead>
<tr>
<th>{% trans 'No.' %}</th>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Username' %}</th>
</tr>
</thead>
<tbody>
{% for account in result.fail_accounts %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ account.asset }}</td>
<td>{{ account.username }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<style>
table {
width: 100%;
border-collapse: collapse;
max-width: 100%;
text-align: left;
margin-top: 10px;
padding: 20px;
}
th {
background: #f2f2f2;
font-size: 14px;
padding: 5px;
border: 1px solid #ddd;
}
tr :first-child {
width: 30%;
}
td {
border: 1px solid #ddd;
padding: 5px;
font-size: 12px;
}
.result {
margin-top: 20px;
}
.result tr :first-child {
width: 10%;
}
</style>

View File

@ -3,74 +3,88 @@
<div class='summary'> <div class='summary'>
<p>{% trans 'The following is a summary of the account check tasks. Please review and handle them' %}</p> <p>{% trans 'The following is a summary of the account check tasks. Please review and handle them' %}</p>
<table> <table>
<caption></caption>
<thead> <thead>
<tr> <tr>
<th colspan='2'>任务汇总: </th> <th colspan='2'>任务汇总:</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{% trans 'Task name' %}: </td> <td>{% trans 'Task name' %}:</td>
<td>{{ execution.automation.name }} </td> <td>{{ execution.automation.name }} </td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Date start' %}: </td> <td>{% trans 'Date start' %}:</td>
<td>{{ execution.date_start }}</td> <td>{{ execution.date_start | date:"Y/m/d H:i:s" }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Date end' %}: </td> <td>{% trans 'Date end' %}:</td>
<td>{{ execution.date_finished }}</td> <td>{{ execution.date_finished | date:"Y/m/d H:i:s" }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Time using' %}: </td> <td>{% trans 'Time using' %}:</td>
<td>{{ execution.duration }}s</td> <td>{{ execution.duration }}s</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Assets count' %}: </td> <td>{% trans 'Assets count' %}:</td>
<td>{{ summary.assets }}</td> <td>{{ summary.assets }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Account count' %}: </td> <td>{% trans 'Asset success count' %}:</td>
<td>{{ summary.accounts }}</td> <td>{{ summary.ok_assets }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Week password count' %}:</td> <td>{% trans 'Asset failed count' %}:</td>
<td> <span> {{ summary.weak_password }}</span></td> <td>{{ summary.fail_assets }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Ok count' %}: </td> <td>{% trans 'Asset not support count' %}:</td>
<td>{{ summary.ok }}</td> <td>{{ summary.error_assets }}</td>
</tr> </tr>
<tr>
<td>{% trans 'No password count' %}: </td> <tr>
<td>{{ summary.no_secret }}</td> <td>{% trans 'Account count' %}:</td>
</tr> <td>{{ summary.accounts }}</td>
</tr>
<tr>
<td>{% trans 'Ok count' %}:</td>
<td>{{ summary.ok }}</td>
</tr>
<tr>
<td>{% trans 'No password count' %}:</td>
<td>{{ summary.no_secret }}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class='result'> <div class='result'>
<p>{% trans 'Account check details' %}:</p> {% if summary.weak_password %}
<table style=""> <p>{% trans 'Week password' %}: {{ summary.weak_password }}</p>
<thead> <p>{% trans 'Account check details' %}:</p>
<tr> <table>
<th>{% trans 'No.' %}</th> <caption></caption>
<th>{% trans 'Asset' %}</th> <thead>
<th>{% trans 'Username' %}</th>
<th>{% trans 'Result' %}</th>
</tr>
</thead>
<tbody>
{% for account in result.weak_password %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <th>{% trans 'No.' %}</th>
<td>{{ account.asset }}</td> <th>{% trans 'Asset' %}</th>
<td>{{ account.username }}</td> <th>{% trans 'Username' %}</th>
<td style="color: red">{% trans 'Week password' %}</td> <th>{% trans 'Result' %}</th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for account in result.weak_password %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ account.asset }}</td>
<td>{{ account.username }}</td>
<td style="color: red">{% trans 'Week password' %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div> </div>
<style> <style>

View File

@ -3,129 +3,123 @@
<div class='summary'> <div class='summary'>
<p>{% trans 'The following is a summary of the account check tasks. Please review and handle them' %}</p> <p>{% trans 'The following is a summary of the account check tasks. Please review and handle them' %}</p>
<table> <table>
<caption></caption>
<thead> <thead>
<tr> <tr>
<th colspan='2'>任务汇总: </th> <th colspan='2'>任务汇总:</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{% trans 'Task name' %}: </td> <td>{% trans 'Task name' %}:</td>
<td>{{ execution.automation.name }} </td> <td>{{ execution.automation.name }} </td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Date start' %}: </td> <td>{% trans 'Date start' %}:</td>
<td>{{ execution.date_start }}</td> <td>{{ execution.date_start | date:"Y/m/d H:i:s" }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Date end' %}: </td> <td>{% trans 'Date end' %}:</td>
<td>{{ execution.date_finished }}</td> <td>{{ execution.date_finished | date:"Y/m/d H:i:s" }}</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Time using' %}: </td> <td>{% trans 'Time using' %}:</td>
<td>{{ execution.duration }}s</td> <td>{{ execution.duration }}s</td>
</tr> </tr>
<tr>
<tr> <td>{% trans 'Assets count' %}:</td>
<td>{% trans 'Assets count' %}: </td> <td>{{ summary.total_assets }}</td>
<td>{{ summary.total_assets }}</td> </tr>
</tr> <tr>
<tr> <td>{% trans 'Asset success count' %}:</td>
<td>{% trans 'Asset success count' %}: </td> <td>{{ summary.ok_assets }}</td>
<td>{{ summary.ok_assets }}</td> </tr>
</tr> <tr>
<tr> <td>{% trans 'Asset failed count' %}:</td>
<td>{% trans 'Asset failed count' %}: </td> <td>{{ summary.fail_assets }}</td>
<td>{{ summary.fail_assets }}</td> </tr>
</tr> <tr>
<tr> <td>{% trans 'Asset not support count' %}:</td>
<td>{% trans 'Asset not support count' %}: </td> <td>{{ summary.error_assets }}</td>
<td>{{ summary.error_assets }}</td> </tr>
</tr>
<tr>
<td>{% trans 'Account new found count' %}: </td>
<td>{{ summary.new_accounts }}</td>
</tr>
<tr>
<td>{% trans 'Account lost count' %}: </td>
<td>{{ summary.lost_accounts }}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class='result'> <div class='result'>
<p>{% trans 'New found accounts' %}: {{ summary.new_accounts }}</p>
{% if summary.new_accounts %} {% if summary.new_accounts %}
<table style=""> <p>{% trans 'New found accounts' %}: {{ summary.new_accounts }}</p>
<thead> <table>
<tr> <caption></caption>
<th>{% trans 'No.' %}</th> <thead>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Username' %}</th>
</tr>
</thead>
<tbody>
{% for account in result.new_accounts %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <th>{% trans 'No.' %}</th>
<td>{{ account.asset }}</td> <th>{% trans 'Asset' %}</th>
<td>{{ account.username }}</td> <th>{% trans 'Username' %}</th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for account in result.new_accounts %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ account.asset }}</td>
<td>{{ account.username }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %} {% endif %}
</div> </div>
<div class='result'> <div class='result'>
<p>{% trans 'Lost accounts' %}: {{ summary.lost_accounts }}</p>
{% if summary.lost_accounts %} {% if summary.lost_accounts %}
<table style=""> <p>{% trans 'Lost accounts' %}: {{ summary.lost_accounts }}</p>
<thead> <table>
<tr> <caption></caption>
<th>{% trans 'No.' %}</th> <thead>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Username' %}</th>
</tr>
</thead>
<tbody>
{% for account in result.lost_accounts %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <th>{% trans 'No.' %}</th>
<td>{{ account.asset }}</td> <th>{% trans 'Asset' %}</th>
<td>{{ account.username }}</td> <th>{% trans 'Username' %}</th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for account in result.lost_accounts %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ account.asset }}</td>
<td>{{ account.username }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %} {% endif %}
</div> </div>
<div class='result'> <div class='result'>
<p>{% trans 'New found risks' %}: {{ summary.new_risks }}</p> <p>{% trans 'New found risks' %}: {{ summary.new_risks }}</p>
{% if summary.new_risks %} {% if summary.new_risks %}
<table style=""> <table>
<thead> <caption></caption>
<tr> <thead>
<th>{% trans 'No.' %}</th>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Username' %}</th>
<th>{% trans 'Result' %}</th>
</tr>
</thead>
<tbody>
{% for risk in result.risks %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <th>{% trans 'No.' %}</th>
<td>{{ risk.asset }}</td> <th>{% trans 'Asset' %}</th>
<td>{{ risk.username }}</td> <th>{% trans 'Username' %}</th>
<td>{{ risk.risk }}</td> <th>{% trans 'Result' %}</th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for risk in result.risks %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ risk.asset }}</td>
<td>{{ risk.username }}</td>
<td>{{ risk.risk }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %} {% endif %}
</div> </div>