diff --git a/router_manager/forms.py b/router_manager/forms.py index 486638e..b5fbb36 100644 --- a/router_manager/forms.py +++ b/router_manager/forms.py @@ -1,10 +1,14 @@ from django import forms from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Submit, Row, Column, HTML -from .models import Router +from .models import Router, RouterGroup, SSHKey +import ipaddress +import socket class RouterForm(forms.ModelForm): + password = forms.CharField(widget=forms.PasswordInput, required=False) + class Meta: model = Router fields = ['name', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled'] @@ -15,6 +19,8 @@ class RouterForm(forms.ModelForm): self.helper.form_method = 'post' if self.instance.pk: delete_html = "Delete" + if self.instance.password: + self.fields['password'].widget.attrs['placeholder'] = '************' else: delete_html = '' self.helper.layout = Layout( @@ -29,7 +35,6 @@ class RouterForm(forms.ModelForm): css_class='form-row' ), 'ssh_key', - 'router_type', 'monitoring', 'enabled', @@ -42,3 +47,121 @@ class RouterForm(forms.ModelForm): css_class='form-row' ) ) + + def clean(self): + cleaned_data = super().clean() + name = cleaned_data.get('name') + ssh_key = cleaned_data.get('ssh_key') + password = cleaned_data.get('password') + address = cleaned_data.get('address') + + if name: + name = name.strip() + cleaned_data['name'] = name + + if ssh_key and password: + raise forms.ValidationError('You must provide a password or an SSH Key, not both') + if not ssh_key and not password and not self.instance.password: + raise forms.ValidationError('You must provide a password or an SSH Key') + + if not password and self.instance.password: + cleaned_data['password'] = self.instance.password + + if ssh_key and not password: + cleaned_data['password'] = '' + + if address: + address = address.lower() + cleaned_data['address'] = address + + try: + socket.gethostbyname(address) + except socket.gaierror: + try: + ipaddress.ip_address(address) + except ValueError: + raise forms.ValidationError('The address field must be a valid hostname or IP address.') + + return cleaned_data + + +class RouterGroupForm(forms.ModelForm): + class Meta: + model = RouterGroup + fields = ['name', 'default_group', 'internal_notes', 'routers'] + widgets = { + 'internal_notes': forms.Textarea(attrs={'rows': 4, 'cols': 40}), # Define como um Textarea simples + } + + def __init__(self, *args, **kwargs): + super(RouterGroupForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + if self.instance.pk: + delete_html = "Delete" + else: + delete_html = '' + self.helper.layout = Layout( + 'name', + 'internal_notes', + 'routers', + 'default_group', + Row( + Column( + Submit('submit', 'Salvar', css_class='btn btn-success'), + HTML(' Back '), + HTML(delete_html), + css_class='col-md-12'), + css_class='form-row' + ) + ) + + def clean(self): + cleaned_data = super().clean() + name = cleaned_data.get('name') + default_group = cleaned_data.get('default_group') + + if name: + name = name.strip() + cleaned_data['name'] = name + + if default_group: + RouterGroup.objects.filter(default_group=True).update(default_group=False) + return cleaned_data + +class SSHKeyForm(forms.ModelForm): + class Meta: + model = SSHKey + fields = ['name', 'public_key', 'private_key'] + widgets = { + 'public_key': forms.Textarea(attrs={'rows': 4, 'cols': 40}), + 'private_key': forms.Textarea(attrs={'rows': 4, 'cols': 40}), + } + + def __init__(self, *args, **kwargs): + super(SSHKeyForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + if self.instance.pk: + delete_html = "Delete" + else: + delete_html = '' + self.helper.layout = Layout( + Row( + Column('name', css_class='form-group col-md-12 mb-0'), + ), + Row( + Column('public_key', css_class='form-group col-md-12 mb-0'), + ), + Row( + Column('private_key', css_class='form-group col-md-12 mb-0'), + ), + Row( + Column( + Submit('submit', 'Salvar', css_class='btn btn-success'), + HTML(' Back '), + HTML(delete_html), + css_class='col-md-12'), + css_class='form-row' + ) + ) diff --git a/router_manager/migrations/0004_router_internal_notes_alter_router_name_routergroup.py b/router_manager/migrations/0004_router_internal_notes_alter_router_name_routergroup.py new file mode 100644 index 0000000..24d2e0e --- /dev/null +++ b/router_manager/migrations/0004_router_internal_notes_alter_router_name_routergroup.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.3 on 2024-03-16 18:27 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('router_manager', '0003_routerstatus'), + ] + + operations = [ + migrations.AddField( + model_name='router', + name='internal_notes', + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='router', + name='name', + field=models.CharField(max_length=100, unique=True), + ), + migrations.CreateModel( + name='RouterGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, unique=True)), + ('internal_notes', models.TextField(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)), + ('routers', models.ManyToManyField(to='router_manager.router')), + ], + ), + ] diff --git a/router_manager/migrations/0005_alter_sshkey_name.py b/router_manager/migrations/0005_alter_sshkey_name.py new file mode 100644 index 0000000..92d6132 --- /dev/null +++ b/router_manager/migrations/0005_alter_sshkey_name.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-16 18:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('router_manager', '0004_router_internal_notes_alter_router_name_routergroup'), + ] + + operations = [ + migrations.AlterField( + model_name='sshkey', + name='name', + field=models.CharField(max_length=100, unique=True), + ), + ] diff --git a/router_manager/migrations/0006_routergroup_default_group.py b/router_manager/migrations/0006_routergroup_default_group.py new file mode 100644 index 0000000..b13ba8a --- /dev/null +++ b/router_manager/migrations/0006_routergroup_default_group.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-16 19:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('router_manager', '0005_alter_sshkey_name'), + ] + + operations = [ + migrations.AddField( + model_name='routergroup', + name='default_group', + field=models.BooleanField(default=False), + ), + ] diff --git a/router_manager/migrations/0007_alter_routergroup_routers.py b/router_manager/migrations/0007_alter_routergroup_routers.py new file mode 100644 index 0000000..49507c8 --- /dev/null +++ b/router_manager/migrations/0007_alter_routergroup_routers.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-16 20:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('router_manager', '0006_routergroup_default_group'), + ] + + operations = [ + migrations.AlterField( + model_name='routergroup', + name='routers', + field=models.ManyToManyField(blank=True, to='router_manager.router'), + ), + ] diff --git a/router_manager/models.py b/router_manager/models.py index 9843ecf..c1165b4 100644 --- a/router_manager/models.py +++ b/router_manager/models.py @@ -3,7 +3,7 @@ import uuid class SSHKey(models.Model): - name = models.CharField(max_length=100) + name = models.CharField(max_length=100, unique=True) public_key = models.TextField() private_key = models.TextField() @@ -16,7 +16,8 @@ class SSHKey(models.Model): class Router(models.Model): - name = models.CharField(max_length=100) + name = models.CharField(max_length=100, unique=True) + internal_notes = models.TextField(null=True, blank=True) address = models.CharField(max_length=15) username = models.CharField(max_length=100, default='admin') password = models.CharField(max_length=100, null=True, blank=True) @@ -44,3 +45,16 @@ class RouterStatus(models.Model): created = models.DateTimeField(auto_now_add=True) uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4) + +class RouterGroup(models.Model): + name = models.CharField(max_length=100, unique=True) + default_group = models.BooleanField(default=False) + internal_notes = models.TextField(null=True, blank=True) + routers = models.ManyToManyField(Router, blank=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 diff --git a/router_manager/views.py b/router_manager/views.py index a362a59..a3061de 100644 --- a/router_manager/views.py +++ b/router_manager/views.py @@ -1,17 +1,28 @@ 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 +from .models import Router, RouterGroup, RouterStatus, SSHKey +from .forms import RouterForm, RouterGroupForm, SSHKeyForm @login_required def view_router_list(request): - router_list = Router.objects.all() + router_list = Router.objects.all().order_by('name') + filter_group = None + if request.GET.get('filter_group'): + if request.GET.get('filter_group') == 'all': + pass + else: + filter_group = get_object_or_404(RouterGroup, uuid=request.GET.get('filter_group')) + router_list = router_list.filter(routergroup=filter_group) + + if not filter_group and request.GET.get('filter_group') != 'all': + filter_group = RouterGroup.objects.filter(default_group=True).first() context = { 'router_list': router_list, 'page_title': 'Router List', - + 'filter_group_list': RouterGroup.objects.all().order_by('name'), + 'filter_group': filter_group, } return render(request, 'router_manager/router_list.html', context=context) @@ -42,4 +53,80 @@ def view_manage_router(request): 'page_title': 'Manage Router', 'instance': router } - return render(request, 'generic_form.html', context=context) \ No newline at end of file + return render(request, 'generic_form.html', context=context) + + +@login_required() +def view_router_group_list(request): + context = { + 'router_group_list': RouterGroup.objects.all().order_by('name'), + 'page_title': 'Router Group List', + } + return render(request, 'router_manager/router_group_list.html', context=context) + + +@login_required() +def view_manage_router_group(request): + if request.GET.get('uuid'): + router_group = get_object_or_404(RouterGroup, uuid=request.GET.get('uuid')) + if request.GET.get('action') == 'delete': + if request.GET.get('confirmation') == 'delete': + router_group.delete() + messages.success(request, 'Router Group deleted successfully') + return redirect('router_group_list') + else: + messages.warning(request, 'Router Group not deleted|Invalid confirmation') + return redirect('router_group_list') + else: + router_group = None + + form = RouterGroupForm(request.POST or None, instance=router_group) + if form.is_valid(): + form.save() + messages.success(request, 'Router Group saved successfully') + return redirect('router_group_list') + + context = { + 'form': form, + 'page_title': 'Manage Router Group', + 'instance': router_group + } + return render(request, 'generic_form.html', context=context) + + +@login_required() +def view_ssh_key_list(request): + context = { + 'sshkey_list': SSHKey.objects.all().order_by('name'), + 'page_title': 'SSH Key List', + } + return render(request, 'router_manager/sshkey_list.html', context=context) + + +@login_required() +def view_manage_sshkey(request): + if request.GET.get('uuid'): + sshkey = get_object_or_404(SSHKey, uuid=request.GET.get('uuid')) + if request.GET.get('action') == 'delete': + if request.GET.get('confirmation') == 'delete': + sshkey.delete() + messages.success(request, 'SSH Key deleted successfully') + return redirect('ssh_keys_list') + else: + messages.warning(request, 'SSH Key not deleted|Invalid confirmation') + return redirect('ssh_keys_list') + else: + sshkey = None + + form = SSHKeyForm(request.POST or None, instance=sshkey) + if form.is_valid(): + form.save() + messages.success(request, 'SSH Key saved successfully') + return redirect('ssh_keys_list') + + context = { + 'form': form, + 'page_title': 'Manage SSH Key', + 'instance': sshkey + } + return render(request, 'generic_form.html', context=context) diff --git a/routerfleet/urls.py b/routerfleet/urls.py index e632163..34b755d 100644 --- a/routerfleet/urls.py +++ b/routerfleet/urls.py @@ -3,7 +3,7 @@ from django.urls import path from dashboard.views import view_dashboard, view_status from user_manager.views import view_manage_user, view_user_list from accounts.views import view_login, view_logout, view_create_first_user -from router_manager.views import view_router_list, view_manage_router +from router_manager.views import view_router_list, view_manage_router, view_router_group_list, view_ssh_key_list, view_manage_router_group, view_manage_sshkey urlpatterns = [ path('admin/', admin.site.urls), @@ -16,4 +16,8 @@ urlpatterns = [ 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'), + path('router/group_list/', view_router_group_list, name='router_group_list'), + path('router/ssh_keys/', view_ssh_key_list, name='ssh_keys_list'), + path('router/manage_group/', view_manage_router_group, name='manage_router_group'), + path('router/manage_sshkey/', view_manage_sshkey, name='manage_sshkey'), ] diff --git a/templates/base.html b/templates/base.html index b2a1243..760c9e2 100644 --- a/templates/base.html +++ b/templates/base.html @@ -23,8 +23,9 @@ - + {% comment %} + {% endcomment %}