- Исправлена незакрытая скобка в _build_test_command (строка 745) - Добавлена поддержка k8s preset'ов: выполнение create_k8s_cluster.py перед create.yml - Обновлены образы в k8s preset'ах: заменен недоступный ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy на inecs/ansible-lab:ubuntu22-latest - Обновлены preset'ы в базе данных через SQL - Обновлены файлы: k8s-single.yml, k8s-multi.yml, k8s-istio-full.yml
179 lines
6.9 KiB
HTML
179 lines
6.9 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Playbook - DevOpsLab{% endblock %}
|
||
{% block page_title %}Playbook{% endblock %}
|
||
|
||
{% block header_actions %}
|
||
<a href="/playbooks/create" class="btn btn-primary btn-sm">
|
||
<i class="fas fa-plus me-2"></i>
|
||
Создать playbook
|
||
</a>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="row g-3" id="playbooks-list">
|
||
{% for playbook in playbooks %}
|
||
<div class="col-12 col-md-6 col-lg-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||
<h5 class="card-title mb-0">
|
||
<a href="/playbooks/{{ playbook.id }}" class="text-decoration-none">
|
||
{{ playbook.name }}
|
||
</a>
|
||
</h5>
|
||
<div class="btn-group">
|
||
<a
|
||
href="/playbooks/{{ playbook.id }}/edit"
|
||
class="btn btn-sm btn-outline-primary"
|
||
title="Редактировать"
|
||
>
|
||
<i class="fas fa-edit"></i>
|
||
</a>
|
||
<button
|
||
onclick="deletePlaybook({{ playbook.id }}, '{{ playbook.name }}', this)"
|
||
class="btn btn-sm btn-outline-danger"
|
||
title="Удалить"
|
||
>
|
||
<i class="fas fa-trash-alt"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{% if playbook.description %}
|
||
<p class="card-text text-muted small mb-3">{{ playbook.description }}</p>
|
||
{% endif %}
|
||
|
||
<div class="small text-muted mb-2">
|
||
<i class="fas fa-tasks me-1"></i>
|
||
Ролей: <span class="fw-semibold">{{ playbook.roles|length }}</span>
|
||
</div>
|
||
{% if playbook.roles %}
|
||
<div class="small text-muted mb-2">
|
||
<i class="fas fa-list me-1"></i>
|
||
Роли:
|
||
{% for role in playbook.roles[:3] %}
|
||
<span class="badge bg-info me-1">{{ role }}</span>
|
||
{% endfor %}
|
||
{% if playbook.roles|length > 3 %}
|
||
<span class="text-muted">+{{ playbook.roles|length - 3 }}</span>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="small text-muted mb-2">
|
||
<i class="fas fa-info-circle me-1"></i>
|
||
Статус:
|
||
<span class="badge {% if playbook.status == 'active' %}bg-success{% else %}bg-secondary{% endif %}">
|
||
{{ playbook.status }}
|
||
</span>
|
||
</div>
|
||
|
||
<div class="d-grid mt-3">
|
||
<a
|
||
href="/playbooks/{{ playbook.id }}"
|
||
class="btn btn-secondary btn-sm"
|
||
>
|
||
<i class="fas fa-info-circle me-1"></i>
|
||
Детали
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="col-12">
|
||
<div class="alert alert-info">
|
||
<i class="fas fa-info-circle me-2"></i>
|
||
Playbook'ов пока нет. <a href="/playbooks/create">Создайте первый playbook</a>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
async function deletePlaybook(playbookId, playbookName, button) {
|
||
// Показываем модальное окно подтверждения
|
||
const confirmed = await showConfirmModal(
|
||
`Вы уверены, что хотите удалить playbook '${playbookName}'?`,
|
||
'Подтверждение удаления'
|
||
);
|
||
|
||
if (!confirmed) {
|
||
return;
|
||
}
|
||
|
||
// Отключаем кнопку во время запроса
|
||
button.disabled = true;
|
||
const originalHTML = button.innerHTML;
|
||
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||
|
||
try {
|
||
const response = await fetch(`/api/v1/playbooks/${playbookId}`, {
|
||
method: 'DELETE',
|
||
credentials: 'include',
|
||
headers: {
|
||
'Accept': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
// Показываем модальное окно с успешным сообщением
|
||
if (window.showMessageModal) {
|
||
window.showMessageModal(
|
||
data.message || `Playbook '${playbookName}' успешно удален`,
|
||
'success',
|
||
'Успешно',
|
||
function() {
|
||
// После закрытия модального окна удаляем карточку
|
||
const card = button.closest('.col-12');
|
||
if (card) {
|
||
card.remove();
|
||
}
|
||
}
|
||
);
|
||
} else {
|
||
// Если функция недоступна, просто удаляем карточку
|
||
const card = button.closest('.col-12');
|
||
if (card) {
|
||
card.remove();
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
// Ошибка - показываем в модальном окне
|
||
try {
|
||
const errorData = await response.json();
|
||
const errorMessage = errorData.detail || errorData.message || 'Ошибка при удалении playbook';
|
||
if (window.showMessageModal) {
|
||
window.showMessageModal(errorMessage, 'error');
|
||
} else {
|
||
alert(errorMessage);
|
||
}
|
||
} catch (e) {
|
||
if (window.showMessageModal) {
|
||
window.showMessageModal('Ошибка при удалении playbook', 'error');
|
||
} else {
|
||
alert('Ошибка при удалении playbook');
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка при удалении playbook:', error);
|
||
if (window.showMessageModal) {
|
||
window.showMessageModal('Ошибка при удалении playbook', 'error');
|
||
} else {
|
||
alert('Ошибка при удалении playbook');
|
||
}
|
||
} finally {
|
||
// Восстанавливаем кнопку
|
||
button.disabled = false;
|
||
button.innerHTML = originalHTML;
|
||
}
|
||
}
|
||
</script>
|
||
{% endblock %}
|