mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-08-03 17:54:38 +02:00
Basic router manager CRUD
This commit is contained in:
parent
c104a12df0
commit
9129a007ee
16 changed files with 403 additions and 2 deletions
0
router_manager/__init__.py
Normal file
0
router_manager/__init__.py
Normal file
29
router_manager/admin.py
Normal file
29
router_manager/admin.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import Router, SSHKey, RouterStatus
|
||||||
|
|
||||||
|
|
||||||
|
class RouterAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'address', 'username', 'router_type', 'enabled', 'monitoring')
|
||||||
|
search_fields = ('name', 'address', 'username', 'router_type')
|
||||||
|
list_filter = ('router_type', 'enabled', 'monitoring')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Router, RouterAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKeyAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'updated', 'created')
|
||||||
|
search_fields = ('name',)
|
||||||
|
list_filter = ('updated', 'created')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(SSHKey, SSHKeyAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterStatusAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('router', 'status_online', 'last_status_change', 'last_backup')
|
||||||
|
search_fields = ('router', 'status_online')
|
||||||
|
list_filter = ('status_online', 'last_status_change', 'last_backup')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(RouterStatus, RouterStatusAdmin)
|
6
router_manager/apps.py
Normal file
6
router_manager/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class RouterManagerConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'router_manager'
|
44
router_manager/forms.py
Normal file
44
router_manager/forms.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
from django import forms
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Layout, Submit, Row, Column, HTML
|
||||||
|
from .models import Router
|
||||||
|
|
||||||
|
|
||||||
|
class RouterForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Router
|
||||||
|
fields = ['name', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(RouterForm, self).__init__(*args, **kwargs)
|
||||||
|
self.helper = FormHelper()
|
||||||
|
self.helper.form_method = 'post'
|
||||||
|
if self.instance.pk:
|
||||||
|
delete_html = "<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>Delete</a>"
|
||||||
|
else:
|
||||||
|
delete_html = ''
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
Row(
|
||||||
|
Column('name', css_class='form-group col-md-6 mb-0'),
|
||||||
|
Column('address', css_class='form-group col-md-6 mb-0'),
|
||||||
|
css_class='form-row'
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
Column('username', css_class='form-group col-md-6 mb-0'),
|
||||||
|
Column('password', css_class='form-group col-md-6 mb-0'),
|
||||||
|
css_class='form-row'
|
||||||
|
),
|
||||||
|
'ssh_key',
|
||||||
|
|
||||||
|
'router_type',
|
||||||
|
'monitoring',
|
||||||
|
'enabled',
|
||||||
|
Row(
|
||||||
|
Column(
|
||||||
|
Submit('submit', 'Salvar', css_class='btn btn-success'),
|
||||||
|
HTML(' <a class="btn btn-secondary" href="/router/list/">Back</a> '),
|
||||||
|
HTML(delete_html),
|
||||||
|
css_class='col-md-12'),
|
||||||
|
css_class='form-row'
|
||||||
|
)
|
||||||
|
)
|
45
router_manager/migrations/0001_initial.py
Normal file
45
router_manager/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-03-15 21:08
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SSHKey',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('public_key', models.TextField()),
|
||||||
|
('private_key', models.TextField()),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Router',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('address', models.CharField(max_length=15)),
|
||||||
|
('username', models.CharField(default='admin', max_length=100)),
|
||||||
|
('password', models.CharField(max_length=100)),
|
||||||
|
('monitoring', models.BooleanField(default=True)),
|
||||||
|
('router_type', models.CharField(choices=[('routeros', 'Mikrotik (RouterOS)'), ('openwrt', 'OpenWRT')], max_length=100)),
|
||||||
|
('enabled', models.BooleanField(default=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
|
('ssh_key', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='router_manager.sshkey')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
18
router_manager/migrations/0002_alter_router_password.py
Normal file
18
router_manager/migrations/0002_alter_router_password.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-03-16 12:36
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('router_manager', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='router',
|
||||||
|
name='password',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True),
|
||||||
|
),
|
||||||
|
]
|
28
router_manager/migrations/0003_routerstatus.py
Normal file
28
router_manager/migrations/0003_routerstatus.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-03-16 15:16
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('router_manager', '0002_alter_router_password'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RouterStatus',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('status_online', models.BooleanField(default=False)),
|
||||||
|
('last_status_change', models.DateTimeField(blank=True, null=True)),
|
||||||
|
('last_backup', models.DateTimeField(blank=True, null=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||||
|
('router', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='router_manager.router')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
router_manager/migrations/__init__.py
Normal file
0
router_manager/migrations/__init__.py
Normal file
46
router_manager/models.py
Normal file
46
router_manager/models.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
from django.db import models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKey(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
public_key = models.TextField()
|
||||||
|
private_key = models.TextField()
|
||||||
|
|
||||||
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Router(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
address = models.CharField(max_length=15)
|
||||||
|
username = models.CharField(max_length=100, default='admin')
|
||||||
|
password = models.CharField(max_length=100, null=True, blank=True)
|
||||||
|
ssh_key = models.ForeignKey(SSHKey, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
monitoring = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
router_type = models.CharField(max_length=100, choices=(('routeros', 'Mikrotik (RouterOS)'), ('openwrt', 'OpenWRT')))
|
||||||
|
enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class RouterStatus(models.Model):
|
||||||
|
router = models.OneToOneField(Router, on_delete=models.CASCADE)
|
||||||
|
status_online = models.BooleanField(default=False)
|
||||||
|
last_status_change = models.DateTimeField(blank=True, null=True)
|
||||||
|
last_backup = models.DateTimeField(blank=True, null=True)
|
||||||
|
|
||||||
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4)
|
||||||
|
|
3
router_manager/tests.py
Normal file
3
router_manager/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
45
router_manager/views.py
Normal file
45
router_manager/views.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from .models import Router
|
||||||
|
from .forms import RouterForm
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_router_list(request):
|
||||||
|
router_list = Router.objects.all()
|
||||||
|
context = {
|
||||||
|
'router_list': router_list,
|
||||||
|
'page_title': 'Router List',
|
||||||
|
|
||||||
|
}
|
||||||
|
return render(request, 'router_manager/router_list.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def view_manage_router(request):
|
||||||
|
if request.GET.get('uuid'):
|
||||||
|
router = get_object_or_404(Router, uuid=request.GET.get('uuid'))
|
||||||
|
if request.GET.get('action') == 'delete':
|
||||||
|
if request.GET.get('confirmation') == 'delete':
|
||||||
|
router.delete()
|
||||||
|
messages.success(request, 'Router deleted successfully')
|
||||||
|
return redirect('router_list')
|
||||||
|
else:
|
||||||
|
messages.warning(request, 'Router not deleted|Invalid confirmation')
|
||||||
|
return redirect('router_list')
|
||||||
|
else:
|
||||||
|
router = None
|
||||||
|
|
||||||
|
form = RouterForm(request.POST or None, instance=router)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
messages.success(request, 'Router saved successfully')
|
||||||
|
return redirect('router_list')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'form': form,
|
||||||
|
'page_title': 'Manage Router',
|
||||||
|
'instance': router
|
||||||
|
}
|
||||||
|
return render(request, 'generic_form.html', context=context)
|
|
@ -39,7 +39,8 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
'crispy_bootstrap4',
|
'crispy_bootstrap4',
|
||||||
'user_manager'
|
'user_manager',
|
||||||
|
'router_manager',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path
|
||||||
from dashboard.views import view_dashboard, view_status
|
from dashboard.views import view_dashboard, view_status
|
||||||
from user_manager.views import view_manage_user, view_user_list
|
from user_manager.views import view_manage_user, view_user_list
|
||||||
from accounts.views import view_login, view_logout, view_create_first_user
|
from accounts.views import view_login, view_logout, view_create_first_user
|
||||||
|
from router_manager.views import view_router_list, view_manage_router
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
@ -14,4 +14,6 @@ urlpatterns = [
|
||||||
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
|
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
|
||||||
path('accounts/login/', view_login, name='login'),
|
path('accounts/login/', view_login, name='login'),
|
||||||
path('accounts/logout/', view_logout, name='logout'),
|
path('accounts/logout/', view_logout, name='logout'),
|
||||||
|
path('router/list/', view_router_list, name='router_list'),
|
||||||
|
path('router/manage/', view_manage_router, name='manage_router'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,6 +25,14 @@
|
||||||
<link rel="stylesheet" href="/static/AdminLTE-3.2.0/plugins/daterangepicker/daterangepicker.css">
|
<link rel="stylesheet" href="/static/AdminLTE-3.2.0/plugins/daterangepicker/daterangepicker.css">
|
||||||
<!-- summernote -->
|
<!-- summernote -->
|
||||||
<link rel="stylesheet" href="/static/AdminLTE-3.2.0/plugins/summernote/summernote-bs4.min.css">
|
<link rel="stylesheet" href="/static/AdminLTE-3.2.0/plugins/summernote/summernote-bs4.min.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.min-width {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
{% block page_custom_head %}{% endblock%}
|
{% block page_custom_head %}{% endblock%}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -93,6 +101,16 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/router/list/" class="nav-link {% if '/router/' in request.path %}active{% endif %}">
|
||||||
|
<i class="fas fa-network-wired nav-icon"></i>
|
||||||
|
<p>
|
||||||
|
Router Manager
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="/user/list/" class="nav-link {% if '/user/' in request.path %}active{% endif %}">
|
<a href="/user/list/" class="nav-link {% if '/user/' in request.path %}active{% endif %}">
|
||||||
<i class="fas fa-users nav-icon"></i>
|
<i class="fas fa-users nav-icon"></i>
|
||||||
|
|
38
templates/generic_form.html
Normal file
38
templates/generic_form.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class='row'>
|
||||||
|
<div class='{% if form_size %}{{ form_size }}{% else %}col-lg-6{% endif %}'>
|
||||||
|
<div class="card card-primary card-outline">
|
||||||
|
{% if page_title %}
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">{{ page_title }}</h3>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="card-body row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% crispy form %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_page_scripts %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function openCommandDialog(element) {
|
||||||
|
var command = element.getAttribute('data-command');
|
||||||
|
var confirmation = prompt("{% if delete_confirmation_message %}{{ delete_confirmation_message }}{% else %}Please type 'delete' to proceed.{% endif %}");
|
||||||
|
if (confirmation) {
|
||||||
|
var url = "?uuid={{ instance.uuid }}&action=delete&confirmation=" + encodeURIComponent(confirmation);
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
78
templates/router_manager/router_list.html
Normal file
78
templates/router_manager/router_list.html
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class='row'>
|
||||||
|
<div class='col-lg-12'>
|
||||||
|
<div class="card card-primary card-outline">
|
||||||
|
{% if page_title %}
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">{{ page_title }}</h3>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Address</th>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Authentication</th>
|
||||||
|
<th></th>
|
||||||
|
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for router in router_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ router.name }}</td>
|
||||||
|
<td>{{ router.get_router_type_display }}</td>
|
||||||
|
<td>{{ router.address }}</td>
|
||||||
|
<td>{{ router.enabled }}</td>
|
||||||
|
<td>
|
||||||
|
{% if router.monitoring %}
|
||||||
|
{% if router.routerstatus.status_online %}
|
||||||
|
Online
|
||||||
|
{% else %}
|
||||||
|
Offline
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
---
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if router.ssh_key %}
|
||||||
|
{{ router.ssh_key }}
|
||||||
|
{% elif router.password %}
|
||||||
|
User/Password
|
||||||
|
{% else %}
|
||||||
|
Missing authentication
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="min-width">
|
||||||
|
<a href="/router/manage/?uuid={{ router.uuid }}"><i class="fas fa-edit"></i></a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<a href="/router/manage/" class="btn btn-primary">Add Router</a>
|
||||||
|
<a href="#" class="btn btn-outline-primary">Router Groups</a>
|
||||||
|
<a href="#" class="btn btn-outline-primary">SSH Keys</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue