init
Some checks failed
Build And Test / build-and-push (push) Failing after 53s

This commit is contained in:
2024-12-13 17:12:03 +09:00
parent a27be94b19
commit ea11832a53
695 changed files with 71766 additions and 1 deletions

View File

8
ansible_manager/admin.py Normal file
View File

@ -0,0 +1,8 @@
from django.contrib import admin
from .models import AnsibleJob
@admin.register(AnsibleJob)
class AnsibleTaskAdmin(admin.ModelAdmin):
list_display = ('name', 'status', 'owner', 'created_at', 'updated_at')
readonly_fields = ('status', 'result')

6
ansible_manager/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AnsibleManagerConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ansible_manager'

12
ansible_manager/forms.py Normal file
View File

@ -0,0 +1,12 @@
from django import forms
from .models import AnsibleJob
class AnsibleJobForm(forms.ModelForm):
class Meta:
model = AnsibleJob
fields = ['name', 'playbook_content', 'inventory_content']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'inventory_content': forms.Textarea(attrs={'class': 'form-control'}),
'playbook_content': forms.Textarea(attrs={'class': 'form-control'}),
}

View File

@ -0,0 +1,31 @@
# Generated by Django 4.2.14 on 2024-12-12 14:20
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='AnsibleTask',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('playbook_file', models.FileField(upload_to='playbooks/')),
('inventory_file', models.FileField(blank=True, null=True, upload_to='inventories/')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('status', models.CharField(choices=[('PENDING', 'Pending'), ('RUNNING', 'Running'), ('SUCCESS', 'Success'), ('FAILED', 'Failed')], default='PENDING', max_length=20)),
('result', models.TextField(blank=True, null=True)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,56 @@
# Generated by Django 4.2.14 on 2024-12-12 22:22
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('ansible_manager', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='ansibletask',
name='created_at',
field=models.DateTimeField(auto_now_add=True, help_text='생성 시간'),
),
migrations.AlterField(
model_name='ansibletask',
name='inventory_file',
field=models.FileField(blank=True, help_text='Ansible Inventory 파일 경로 (선택 사항)', null=True, upload_to='ansible/inventories/'),
),
migrations.AlterField(
model_name='ansibletask',
name='name',
field=models.CharField(help_text='작업 이름', max_length=200),
),
migrations.AlterField(
model_name='ansibletask',
name='owner',
field=models.ForeignKey(help_text='작업 소유자', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='ansibletask',
name='playbook_file',
field=models.FileField(help_text='Ansible Playbook 파일 경로', upload_to='ansible/playbooks/'),
),
migrations.AlterField(
model_name='ansibletask',
name='result',
field=models.TextField(blank=True, help_text='실행 결과', null=True),
),
migrations.AlterField(
model_name='ansibletask',
name='status',
field=models.CharField(choices=[('PENDING', 'Pending'), ('RUNNING', 'Running'), ('SUCCESS', 'Success'), ('FAILED', 'Failed')], default='PENDING', help_text='작업 상태', max_length=20),
),
migrations.AlterField(
model_name='ansibletask',
name='updated_at',
field=models.DateTimeField(auto_now=True, help_text='업데이트 시간'),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 4.2.14 on 2024-12-12 22:35
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('ansible_manager', '0002_alter_ansibletask_created_at_and_more'),
]
operations = [
migrations.CreateModel(
name='AnsibleJob',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('playbook_content', models.TextField(help_text='Ansible Playbook YAML 내용')),
('inventory_content', models.TextField(help_text='Ansible Inventory 내용')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('status', models.CharField(choices=[('PENDING', 'Pending'), ('RUNNING', 'Running'), ('SUCCESS', 'Success'), ('FAILED', 'Failed')], default='PENDING', max_length=20)),
('result', models.TextField(blank=True, null=True)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.DeleteModel(
name='AnsibleTask',
),
]

View File

23
ansible_manager/models.py Normal file
View File

@ -0,0 +1,23 @@
from django.db import models
from django.conf import settings
class AnsibleJob(models.Model):
"""Ansible 작업 관리 모델"""
STATUS_CHOICES = [
('PENDING', 'Pending'),
('RUNNING', 'Running'),
('SUCCESS', 'Success'),
('FAILED', 'Failed'),
]
name = models.CharField(max_length=200)
playbook_content = models.TextField(help_text="Ansible Playbook YAML 내용")
inventory_content = models.TextField(help_text="Ansible Inventory 내용")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING')
result = models.TextField(blank=True, null=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.name

View File

@ -0,0 +1,17 @@
{% extends "components/base.html" %}
{% block main_area %}
<h1>Create Ansible Job</h1>
<form method="POST">
{% csrf_token %}
<label for="id_name">Name:</label>
{{ form.name }}
<label for="id_inventory_content">Inventory content:</label>
{{ form.inventory_content }}
<label for="id_playbook_content">Playbook content:</label>
{{ form.playbook_content }}
<button type="submit" class="btn btn-primary">Create</button>
</form>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "components/base.html" %}
{% block main_area %}
<h1>Edit Ansible Job</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save Changes</button>
<a href="{% url 'ansible_manager:job_list' %}" class="btn btn-secondary">Cancel</a>
</form>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends "components/base.html" %}
{% block main_area %}
<h1>Job Details: {{ job.name }}</h1>
<p><strong>Status:</strong> {{ job.status }}</p>
<p><strong>Created At:</strong> {{ job.created_at }}</p>
<p><strong>Updated At:</strong> {{ job.updated_at }}</p>
<h3>Result:</h3>
<pre>{{ job.result|escape|linebreaksbr|default:"No result yet." }}</pre>
<a href="{% url 'ansible_manager:job_list' %}" class="btn btn-secondary">Back to List</a>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "components/base.html" %}
{% block main_area %}
<h1>Ansible Jobs</h1>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Status</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for job in jobs %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ job.name }}</td>
<td>{{ job.status }}</td>
<td>{{ job.created_at }}</td>
<td>
<a href="{% url 'ansible_manager:job_detail' job.id %}" class="btn btn-info">View</a>
<a href="{% url 'ansible_manager:run_job' job.id %}" class="btn btn-success">Run</a>
<a href="{% url 'ansible_manager:edit_job' job.id %}" class="btn btn-warning">Edit</a>
<a href="{% url 'ansible_manager:delete_job' job.id %}" class="btn btn-danger">Delete</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5">No jobs available.</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{% url 'ansible_manager:create_job' %}" class="btn btn-primary">Create New Job</a>
{% endblock %}

3
ansible_manager/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
ansible_manager/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.urls import path
from . import views
app_name = 'ansible_manager'
urlpatterns = [
path('create/', views.create_ansible_job, name='create_job'),
path('jobs/<int:job_id>/edit/', views.edit_ansible_job, name='edit_job'),
path('jobs/', views.job_list, name='job_list'),
path('jobs/<int:job_id>/', views.job_detail, name='job_detail'),
path('jobs/<int:job_id>/run/', views.run_job, name='run_job'),
path('jobs/<int:job_id>/delete/', views.delete_job, name='delete_job'),
]

118
ansible_manager/views.py Normal file
View File

@ -0,0 +1,118 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import AnsibleJob
from .forms import AnsibleJobForm
import subprocess
import tempfile
import os
def run_ansible_job(request, job):
user = request.user
decrypted_key = ""
if user.encrypted_private_key:
try:
decrypted_key = user.decrypt_private_key().strip().replace("\r", "")
except Exception as e:
job.result = f"SSH 키 복호화 오류: {str(e)}"
job.status = 'FAILED'
job.save()
return
job.status = 'RUNNING'
job.save()
try:
private_key = decrypted_key
playbook_file = tempfile.NamedTemporaryFile(delete=False, mode="w", newline='')
inventory_file = tempfile.NamedTemporaryFile(delete=False, mode="w", newline='')
private_key_file = tempfile.NamedTemporaryFile(delete=False, mode="w", newline='')
try:
playbook_file.write(job.playbook_content.strip())
playbook_file.close()
inventory_file.write(job.inventory_content.strip())
inventory_file.close()
private_key_file.write(f"{private_key}\n")
private_key_file.close()
os.chmod(private_key_file.name, 0o600)
command = [
"ansible-playbook", playbook_file.name,
"-i", inventory_file.name,
"--private-key", private_key_file.name,
# "-vvv"
]
result = subprocess.run(command, capture_output=True, text=True)
job.result = result.stdout
job.status = 'SUCCESS' if result.returncode == 0 else 'FAILED'
finally:
os.remove(playbook_file.name)
os.remove(inventory_file.name)
os.remove(private_key_file.name)
except Exception as e:
job.result = f"오류 발생: {str(e)}"
job.status = 'FAILED'
job.save()
@login_required
def create_ansible_job(request):
if request.method == 'POST':
form = AnsibleJobForm(request.POST)
if form.is_valid():
job = form.save(commit=False)
job.owner = request.user
job.status = 'PENDING'
job.save()
return redirect('ansible_manager:job_list')
else:
form = AnsibleJobForm()
return render(request, 'ansible_manager/create_job.html', {'form': form})
@login_required
def edit_ansible_job(request, job_id):
job = get_object_or_404(AnsibleJob, id=job_id, owner=request.user)
if request.method == 'POST':
form = AnsibleJobForm(request.POST, instance=job)
if form.is_valid():
job = form.save(commit=False)
job.owner = request.user
job.status = 'PENDING'
job.save()
# return redirect('ansible_manager:job_detail', job_id=job.id)
return redirect('ansible_manager:job_list')
else:
form = AnsibleJobForm(instance=job)
return render(request, 'ansible_manager/edit_job.html', {'form': form, 'job': job})
@login_required
def job_list(request):
jobs = AnsibleJob.objects.filter(owner=request.user).order_by('-created_at')
return render(request, 'ansible_manager/job_list.html', {'jobs': jobs})
@login_required
def run_job(request, job_id):
job = get_object_or_404(AnsibleJob, id=job_id, owner=request.user)
run_ansible_job(request, job)
return redirect('ansible_manager:job_detail', job_id=job.id)
@login_required
def job_detail(request, job_id):
job = get_object_or_404(AnsibleJob, id=job_id, owner=request.user)
return render(request, 'ansible_manager/job_detail.html', {'job': job})
@login_required
def delete_job(request, job_id):
job = get_object_or_404(AnsibleJob, id=job_id, owner=request.user)
job.delete()
return redirect('ansible_manager:job_list')