diff --git a/docs/model/netbox_sys_provider_credentials.md b/docs/model/netbox_sys_provider_credentials.md index 11f23c64d924f26540fb0f0b32c6bf7f83d9ed82..0876ed043e103bdce478b888d66a66c0c162ea16 100644 --- a/docs/model/netbox_sys_provider_credentials.md +++ b/docs/model/netbox_sys_provider_credentials.md @@ -9,4 +9,4 @@ | assigned_object_id | Big (8 byte) integer | cluster type unique ID | | provider_url | String | Virtual Machine type name | | provider_name | String | Virtual Machine type description | -| provider_password | String | Virtual Machine type description | \ No newline at end of file +| provider_password_vault_url | String | Virtual Machine type description | \ No newline at end of file diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 3ad0b687e476a94d22c30813795d90084b035353..4e7d71252af3e90c88eafaed2385eccbbb202f86 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from virtualization.models import VirtualMachine -from .. models import VirtualMachineMaintenance +from virtualization.models import VirtualMachine, Cluster +from .. models import VirtualMachineMaintenance, ProviderCredentials from django.contrib.contenttypes.models import ContentType class VirtualMachineSerializer(serializers.ModelSerializer): @@ -8,6 +8,11 @@ class VirtualMachineSerializer(serializers.ModelSerializer): model = VirtualMachine fields = ['id', 'name'] +class ClusterSerializer(serializers.ModelSerializer): + class Meta: + model = Cluster + fields = ['id', 'name'] + class VirtualMachineMaintenanceSerializer(serializers.ModelSerializer): id = serializers.IntegerField(read_only=True) virtual_machine = VirtualMachineSerializer(source='assigned_object', read_only=True) @@ -18,3 +23,14 @@ class VirtualMachineMaintenanceSerializer(serializers.ModelSerializer): model = VirtualMachineMaintenance #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine'] fields = '__all__' + +class ProviderCredentialsSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(read_only=True) + cluster = ClusterSerializer(source='assigned_object', read_only=True) + assigned_object_id = serializers.IntegerField(write_only=True) + assigned_object_type = serializers.CharField(write_only=True) + + class Meta: + model = ProviderCredentials + #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine'] + fields = '__all__' \ No newline at end of file diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py index bdba566512a75e3d3584f94aa739f0ba8f354b3e..6f3fb348f4a9948d63374a4e95b2368f9c804c42 100644 --- a/netbox_sys_plugin/api/urls.py +++ b/netbox_sys_plugin/api/urls.py @@ -1,8 +1,9 @@ from rest_framework.routers import DefaultRouter -from .views import VirtualMachineMaintenanceViewSet +from .views import VirtualMachineMaintenanceViewSet, ProviderCredentialsViewSet router = DefaultRouter() router.register(r'MaintenanceWindow', VirtualMachineMaintenanceViewSet) +router.register(r'ProviderCredentials', ProviderCredentialsViewSet) app_name = "netbox_sys_plugin" diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py index 473e1438163c21182329b8fb7102c574fa27196d..b3753336d640c500ab179f011441ee3f6ee6fd5a 100644 --- a/netbox_sys_plugin/api/views.py +++ b/netbox_sys_plugin/api/views.py @@ -1,8 +1,8 @@ from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.response import Response -from .. models import VirtualMachineMaintenance -from . serializers import VirtualMachineMaintenanceSerializer +from .. models import VirtualMachineMaintenance, ProviderCredentials +from . serializers import VirtualMachineMaintenanceSerializer, ProviderCredentialsSerializer class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet): queryset = VirtualMachineMaintenance.objects.all() @@ -10,3 +10,7 @@ class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet): http_method_names = ["get", "post", "patch", "delete", "options"] +class ProviderCredentialsViewSet(viewsets.ModelViewSet): + queryset = ProviderCredentials.objects.all() + serializer_class = ProviderCredentialsSerializer + http_method_names = ["get", "post", "patch", "delete", "options"] \ No newline at end of file diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py index 5748c462510a31c402f1dc389a7f55fe5433543f..75a77e077fc1b3c897ee22dd79dbd8df3d567bbf 100644 --- a/netbox_sys_plugin/filtersets.py +++ b/netbox_sys_plugin/filtersets.py @@ -2,8 +2,9 @@ import django_filters from netbox.filtersets import NetBoxModelFilterSet -from virtualization.models import VirtualMachine -from .models import VirtualMachineMaintenance +from virtualization.models import VirtualMachine, Cluster +from django.db.models import Q +from .models import VirtualMachineMaintenance, ProviderCredentials class VmMaintenanceFilterSet(NetBoxModelFilterSet): @@ -24,4 +25,28 @@ class VmMaintenanceFilterSet(NetBoxModelFilterSet): # pylint: disable=W0613 def search(self, queryset, name, value): """override""" - queryset.filter(virtual_machine__icontains=value) + if not value.strip(): + return queryset + return queryset.filter(Q(maintenance_window__icontains=value)) + +class ProviderCredentialsFilterSet(NetBoxModelFilterSet): + """ProviderCredentials filterset definition class""" + + cluster = django_filters.ModelMultipleChoiceFilter( + field_name="Cluster__name", + queryset=Cluster.objects.all(), + to_field_name="name", + label="Virtual Machine (name)", + ) + + class Meta: + """Meta class""" + model = ProviderCredentials + fields = ('provider_url','provider_username','provider_password_vault_url','cluster') + + # pylint: disable=W0613 + def search(self, queryset, name, value): + """override""" + if not value.strip(): + return queryset + return queryset.filter(Q(provider_username__icontains=value)|Q(provider_url__icontains=value)|Q(provider_password_vault_url__icontains=value)) \ No newline at end of file diff --git a/netbox_sys_plugin/forms/__init__.py b/netbox_sys_plugin/forms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0f3ba6e3f9a49b62cca63d22474239cfb7bb79a3 --- /dev/null +++ b/netbox_sys_plugin/forms/__init__.py @@ -0,0 +1,4 @@ +"""Forms definitions""" + +from .maintenance import * +from .provider import * \ No newline at end of file diff --git a/netbox_sys_plugin/forms.py b/netbox_sys_plugin/forms/maintenance.py similarity index 88% rename from netbox_sys_plugin/forms.py rename to netbox_sys_plugin/forms/maintenance.py index d8dd18941c58355ad705765cd8a48bb9ade67502..46618c6ba442ab8ea7e2eb7b996e1b8b480b328a 100644 --- a/netbox_sys_plugin/forms.py +++ b/netbox_sys_plugin/forms/maintenance.py @@ -11,7 +11,7 @@ from netbox.forms import ( NetBoxModelFilterSetForm, ) from utilities.forms.fields import DynamicModelChoiceField -from .models import VirtualMachineMaintenance +from ..models import VirtualMachineMaintenance class VmMaitenanceForm(NetBoxModelForm): """ @@ -41,19 +41,16 @@ class VmMaitenanceForm(NetBoxModelForm): current_vm_id = instance.assigned_object.id if instance.assigned_object else None else: current_vm_id = None - assigned_vms = VirtualMachineMaintenance.objects.filter( assigned_object_type=ContentType.objects.get_for_model(VirtualMachine) - #).values_list('assigned_object_id', flat=True) ).exclude(assigned_object_id=current_vm_id).values_list('assigned_object_id', flat=True) - self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_vms) class Meta: """Meta class""" model = VirtualMachineMaintenance - fields = ( 'virtual_machine','maintenance_window') + fields = ( 'virtual_machine','maintenance_window','tags') def clean_virtual_machine(self): @@ -65,9 +62,7 @@ class VmMaitenanceForm(NetBoxModelForm): assigned_object_type=virtual_machine_ct, assigned_object_id=virtual_machine.id ).exclude(pk=self.instance.pk).exists(): - raise ValidationError( - "The virtual Machine already has a maintenance windows associates" - ) + raise ValidationError() return virtual_machine @@ -86,7 +81,7 @@ class VmMaitenanceForm(NetBoxModelForm): #Check if VM is assigned corretly if (not virtual_machine): raise ValidationError( - {"__all__": "Virtual Machine already with maintenance window"}, + {"__all__": "A Virtual Machine needs to be associated with the maintenance window"}, ) @@ -101,4 +96,4 @@ class VmMaitenanceForm(NetBoxModelForm): class VmMaintenanceFilterForm(NetBoxModelFilterSetForm): """MacAddress filter form definition class""" - model = VirtualMachineMaintenance + model = VirtualMachineMaintenance \ No newline at end of file diff --git a/netbox_sys_plugin/forms/provider.py b/netbox_sys_plugin/forms/provider.py new file mode 100644 index 0000000000000000000000000000000000000000..1909d38f09c73b4d7ef2584810b144cfa9c7ad23 --- /dev/null +++ b/netbox_sys_plugin/forms/provider.py @@ -0,0 +1,98 @@ +"""Forms definitions""" + +from virtualization.models import Cluster +from django.contrib.contenttypes.models import ContentType +from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError +from django import forms +from netbox.forms import ( + NetBoxModelForm, + NetBoxModelFilterSetForm, +) +from utilities.forms.fields import DynamicModelChoiceField +from ..models import ProviderCredentials + +class ProviderCredentialsForm(NetBoxModelForm): + """ + GUI form to add or edit a Provider Credentials. + """ + + cluster = DynamicModelChoiceField( + queryset=Cluster.objects.all(), required=True, label="Cluster" + ) + provider_url = forms.CharField( + max_length=200, min_length=1, required=True, label="URL" + ) + provider_username = forms.CharField( + max_length=50, min_length=1, required=True, label="Username" + ) + provider_password_vault_url = forms.CharField( + max_length=200, min_length=1, required=True, label="Password Vault URL" + ) + + def __init__(self, *args, **kwargs): + # Initialize helper selectors + instance = kwargs.get("instance") + initial = kwargs.get("initial", {}).copy() + if instance: + if isinstance(instance.assigned_object, Cluster): + initial["cluster"] = instance.assigned_object + kwargs["initial"] = initial + super().__init__(*args, **kwargs) + + if instance: + current_cluster_id = instance.assigned_object.id if instance.assigned_object else None + else: + current_cluster_id = None + + assigned_clusters = ProviderCredentials.objects.filter( + assigned_object_type=ContentType.objects.get_for_model(Cluster) + ).exclude(assigned_object_id=current_cluster_id).values_list('assigned_object_id', flat=True) + self.fields['cluster'].queryset = Cluster.objects.exclude(id__in=assigned_clusters) + + class Meta: + """Meta class""" + model = ProviderCredentials + fields = ('cluster','provider_url', 'provider_username', 'provider_password_vault_url','tags') + + def clean_cluster(self): + + cluster = self.cleaned_data.get("cluster") + + cluster_ct = ContentType.objects.get_for_model(Cluster) + + if ProviderCredentials.objects.filter( + assigned_object_type=cluster_ct, + assigned_object_id=cluster.id + ).exclude(pk=self.instance.pk).exists(): + raise ValidationError() + return cluster + + def clean(self): + """ + Validates form inputs before submitting: + """ + super().clean() + + if self.errors.get(f"provider_url"): + return + + cluster = self.cleaned_data.get("cluster") + + #Check if cluster is assigned corretly + if (not cluster): + raise ValidationError( + {"__all__": "A Virtual Machine needs to be associated with the maintenance window"}, + ) + + def save(self, *args, **kwargs): + # Set assigned object + self.instance.assigned_object = ( + self.cleaned_data.get("cluster") + ) + return super().save(*args, **kwargs) + +class ProviderCredentialsFilterForm(NetBoxModelFilterSetForm): + """Provider Credentials filter form definition class""" + + model = ProviderCredentials \ No newline at end of file diff --git a/netbox_sys_plugin/migrations/0002_alter_virtualmachinemaintenance_maintenance_window_and_more.py b/netbox_sys_plugin/migrations/0002_alter_virtualmachinemaintenance_maintenance_window_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..64b5bde1f21c2676fc5faa8026acf664742c97e0 --- /dev/null +++ b/netbox_sys_plugin/migrations/0002_alter_virtualmachinemaintenance_maintenance_window_and_more.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.16 on 2024-11-18 15:20 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers +import utilities.json + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ('netbox_sys_plugin', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='virtualmachinemaintenance', + name='maintenance_window', + field=models.CharField(blank=True, default=None, max_length=200, null=True, validators=[django.core.validators.RegexValidator(message='Incorrect maintenance window format', regex='(^([0-6])-[0-1]?[0-9]|2[0-3]):[0-5][0-9]$')]), + ), + migrations.CreateModel( + name='ProviderCredentials', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), + ('provider_url', models.CharField(blank=True, default=None, max_length=100, null=True, validators=[django.core.validators.URLValidator(schemes=['http', 'https'])])), + ('provider_username', models.CharField(blank=True, default=None, max_length=50, null=True)), + ('provider_password_vault_url', models.CharField(blank=True, default=None, max_length=200, null=True, validators=[django.core.validators.URLValidator(schemes=['http', 'https'])])), + ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)), + ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'Cluster'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'Provider Credential', + 'verbose_name_plural': 'Provider Credentials', + 'ordering': ['assigned_object_id'], + 'unique_together': {('assigned_object_id',)}, + }, + ), + ] diff --git a/netbox_sys_plugin/models/__init__.py b/netbox_sys_plugin/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cef638b62a297a72c847e8871ce914dc13626cd9 --- /dev/null +++ b/netbox_sys_plugin/models/__init__.py @@ -0,0 +1,4 @@ +"""Models definitions""" + +from .maintenance import VirtualMachineMaintenance +from .provider import ProviderCredentials \ No newline at end of file diff --git a/netbox_sys_plugin/models.py b/netbox_sys_plugin/models/maintenance.py similarity index 96% rename from netbox_sys_plugin/models.py rename to netbox_sys_plugin/models/maintenance.py index e3c1bf8b5ec802ed18169f5a5086c536bddcc425..53e1a81b02c7ca91c37503c2825a5f0b86e1fca5 100644 --- a/netbox_sys_plugin/models.py +++ b/netbox_sys_plugin/models/maintenance.py @@ -51,7 +51,7 @@ class VirtualMachineMaintenance(NetBoxModel): def __str__(self): - return f"{self.maintenance_window}" + return f"For Virtual machine ID {self.assigned_object_id}" def get_absolute_url(self): """override""" diff --git a/netbox_sys_plugin/models/provider.py b/netbox_sys_plugin/models/provider.py new file mode 100644 index 0000000000000000000000000000000000000000..eb1cc24a5d91c566cc457670d9b0ea6b5cf0afe1 --- /dev/null +++ b/netbox_sys_plugin/models/provider.py @@ -0,0 +1,74 @@ +"""Models definitions""" + +from django.core.validators import ( + RegexValidator, +) +from django.urls import reverse +from django.core.validators import ( + URLValidator, +) # pylint: disable=import-error +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.db.models import Q +from virtualization.models import Cluster +from netbox.models import NetBoxModel + +CLUSTER_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="Cluster")) + + + +class ProviderCredentials(NetBoxModel): + """Cluster ProviderInfo definition class""" + + provider_url = models.CharField( + default=None, blank=True, null=True, + max_length=100, + validators=[URLValidator(schemes=["http", "https"])], + ) + + provider_username = models.CharField( + default=None, blank=True, null=True, + max_length=50) + + provider_password_vault_url = models.CharField( + default=None, blank=True, null=True, + max_length=200, + validators=[URLValidator(schemes=["http", "https"])], + ) + + assigned_object_type = models.ForeignKey( + to=ContentType, + limit_choices_to=CLUSTER_ASSIGNMENT_MODELS, + on_delete=models.PROTECT, + null=True, + blank=True, + ) + assigned_object_id = models.PositiveBigIntegerField(null=True, blank=True) + assigned_object = GenericForeignKey( + ct_field="assigned_object_type", + fk_field="assigned_object_id", + ) + + class Meta: + """Meta class""" + unique_together = ["assigned_object_id"] + ordering = ["assigned_object_id"] + verbose_name = "Provider Credential" + verbose_name_plural = "Provider Credentials" + + + def __str__(self): + return f"for Cluster ID {self.assigned_object_id}" + + def get_absolute_url(self): + """override""" + return reverse("plugins:netbox_sys_plugin:providercredentials", args=[self.pk]) + +GenericRelation( + to=ProviderCredentials, + content_type_field="assigned_object_type", + object_id_field="assigned_object_id", + related_query_name="Cluster", +).contribute_to_class(Cluster, "ProviderCredentials") + diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py index a7dd5e8d46277e62fff66e5b88cccb74c6208046..2a39a83fbd5c89ea6965ea9b76e2ad64e36c6b07 100644 --- a/netbox_sys_plugin/navigation.py +++ b/netbox_sys_plugin/navigation.py @@ -10,16 +10,16 @@ vm_maintenance_buttons = [ icon_class="mdi mdi-plus-thick", color=ButtonColorChoices.GREEN, ), - #PluginMenuButton( - # link="plugins:netbox_sys_plugin:virtualmachinemaintenance_add", - # title="Import", - # icon_class="mdi mdi-upload", - # color=ButtonColorChoices.CYAN, - # #permissions=["netbox_sys_plugin.add_macaddress"], - #), ] - +cluster_provider_credentials_buttons = [ + PluginMenuButton( + link="plugins:netbox_sys_plugin:providercredentials_add", + title="Add", + icon_class="mdi mdi-plus-thick", + color=ButtonColorChoices.GREEN, + ), +] vmMaintenanceItem = [ PluginMenuItem( @@ -29,9 +29,19 @@ vmMaintenanceItem = [ ), ] +clusterProviderCredentialsItem = [ + PluginMenuItem( + link="plugins:netbox_sys_plugin:providercredentials_list", + link_text="Provider Credentials", + buttons=cluster_provider_credentials_buttons, + ), +] + menu = PluginMenu( label="Sys", - groups=(("Maintenance", vmMaintenanceItem),) - , + groups=( + ("Maintenance", vmMaintenanceItem), + ("Provider", clusterProviderCredentialsItem), + ), icon_class="mdi mdi-all-inclusive-box-outline", ) diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py index f3cb63b24888010a9f48ba51b6968d6a1fd325a2..bea34c6f4dbb4a0631e26a78ee9ea3c4157d69fb 100644 --- a/netbox_sys_plugin/tables.py +++ b/netbox_sys_plugin/tables.py @@ -2,7 +2,7 @@ import django_tables2 as tables from netbox.tables import NetBoxTable, columns -from .models import VirtualMachineMaintenance +from .models import VirtualMachineMaintenance, ProviderCredentials class VmMaintenanceTable(NetBoxTable): @@ -34,3 +34,48 @@ class VmMaintenanceTable(NetBoxTable): ) default_columns = ('id','maintenance_window','assigned_object') + + +class ProviderCredentialsTable(NetBoxTable): + """VM Maintenance Table definition class""" + + pk = columns.ToggleColumn() + id = tables.Column( + linkify=False, + ) + + assigned_object = tables.Column( + linkify=True, + orderable=False, + verbose_name="Cluster", + ) + + provider_url = tables.Column( + linkify=True, + verbose_name="URL", + ) + provider_username = tables.Column( + linkify=False, + verbose_name="Username", + + ) + provider_password_vault_url = tables.Column( + linkify=False, + verbose_name="Password Vault URL", + ) + + class Meta(NetBoxTable.Meta): + """Meta class""" + model = ProviderCredentials + fields = ( + "pk", + "id", + "provider_url", + "provider_username", + "provider_password_vault_url", + "assigned_object", + "created", + "last_updated", + ) + + default_columns = ('id','provider_url','provider_username','provider_password_vault_url','assigned_object') diff --git a/netbox_sys_plugin/template_content.py b/netbox_sys_plugin/template_content.py index 7ff699e15e4efa75c2f7d247a707af77cb6f99f5..d23d99bd4e02badcbcab133fc3d8a6dca8defe0f 100644 --- a/netbox_sys_plugin/template_content.py +++ b/netbox_sys_plugin/template_content.py @@ -11,6 +11,14 @@ class VmMaintenanceTable(PluginTemplateExtension): def left_page(self): return self.render('netbox_sys_plugin/vm_vmmaintenance_table.html', extra_context={'vmmaintenanceinfo': models.VirtualMachineMaintenance.objects.filter(VirtualMachine=self.context['object'])}) + +class ProviderCredentialsTable(PluginTemplateExtension): + """Provider Credentials object template""" + model = 'virtualization.cluster' -template_extensions = [VmMaintenanceTable] + def right_page(self): + return self.render('netbox_sys_plugin/cluster_providercredentials.html', extra_context={'providercredentialsinfo': models.ProviderCredentials.objects.filter(Cluster=self.context['object'])}) + + +template_extensions = [VmMaintenanceTable,ProviderCredentialsTable] diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html b/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html new file mode 100644 index 0000000000000000000000000000000000000000..82082e0f10bbd4fc3e959becf040866bb8d431c1 --- /dev/null +++ b/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html @@ -0,0 +1,47 @@ +{% block content %} + <div class="row mb-3"> + <div class="col col-md-12"> + <div class="card"> + <h5 class="card-header">Provider Credentials</h5> + <div class="card-body"> + <table class="table table-responsive"> + <th scope="row">URL</th> + <th scope="row">Username</th> + <th scope="row">Passworl Vault URL</th> + {% for providercredentials in providercredentialsinfo %} + <tr> + <td> + {% if providercredentials.provider_url %} + <a href="{{ providercredentials.get_absolute_url }}">{{ providercredentials.provider_url }}</a> + {% else %} + {{ ''|placeholder }} + {% endif %} + </td> + <td> + {% if providercredentials.provider_username %} + {{ providercredentials.provider_username }} + {% else %} + {{ ''|placeholder }} + {% endif %} + </td> + <td> + {% if providercredentials.provider_password_vault_url %} + {{ providercredentials.provider_password_vault_url }} + {% else %} + {{ ''|placeholder }} + {% endif %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="3">No maintenance window found.</td> + </tr> + {% endfor %} + </tr> + </table> + </div> + </div> + {% include 'inc/panels/custom_fields.html' %} + </div> + </div> +{% endblock content %} \ No newline at end of file diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html b/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html new file mode 100644 index 0000000000000000000000000000000000000000..d18f35b004c9e67b845566e983b4b27eda0550a3 --- /dev/null +++ b/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html @@ -0,0 +1,52 @@ +{% extends 'generic/object.html' %} + +{% block title%} +Provider Credentials for {{ object.assigned_object }} +{% endblock title%} + +{% block content %} +<div class="row mb-3"> + <div class="col col-md-6"> + <div class="card"> + <h5 class="card-header">Provider Credentials</h5> + <div class="card-body"> + <table class="table table-hover attr-table"> + <tr> + <th scope="row">URL</th> + <td>{{ object.provider_url }}</td> + </tr> + <tr> + <th scope="row">Username</th> + <td>{{ object.provider_username }}</td> + </tr> + <tr> + <th scope="row">Password vault URL</th> + <td>{{ object.provider_password_vault_url }}</td> + </tr> + </table> + </div> + </div> + {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + </div> + <div class="col col-md-6"> + <div class="card"> + <h5 class="card-header">Assigned Object</h5> + <div class="card-body"> + <table class="table table-hover attr-table"> + <tr> + <th scope="row">{{ object.assigned_object_type|cut:"virtualization | " }}</th> + <td> + {% if object.assigned_object %} + <a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a> + {% else %} + {{ ''|placeholder }} + {% endif %} + </td> + </tr> + </table> + </div> + </div> + </div> +</div> +{% endblock content %} diff --git a/netbox_sys_plugin/urls.py b/netbox_sys_plugin/urls.py index 8d9e94d8a46d390adf95d2545b7ee110e0e14235..4d3e076563d02898711c6e3714bf759eeb8dbf4f 100644 --- a/netbox_sys_plugin/urls.py +++ b/netbox_sys_plugin/urls.py @@ -17,5 +17,17 @@ urlpatterns = ( path('vm-maintenance/<int:pk>/journal/', ObjectJournalView.as_view(), name='virtualmachinemaintenance_journal', kwargs={ 'model': models.VirtualMachineMaintenance }), + path('provider-credentials/', views.ProviderCredentialsListView.as_view(), name='providercredentials_list'), + path('provider-credentials/add/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_add'), + path('provider-credentials/<int:pk>/', views.ProviderCredentialsView.as_view(), name='providercredentials'), + path('provider-credentials/<int:pk>/edit/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_edit'), + path('provider-credentials/<int:pk>/delete/', views.ProviderCredentialsDeleteView.as_view(), name='providercredentials_delete'), + path('provider-credentials/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providercredentials_changelog', kwargs={ + 'model': models.VirtualMachineMaintenance + }), + path('provider-credentials/<int:pk>/journal/', ObjectJournalView.as_view(), name='providercredentials_journal', kwargs={ + 'model': models.VirtualMachineMaintenance + }), + ) diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py index e4103f009c99aefb8c0eb7580faf130d062c18c8..f80387da4da95bb779f063d16729e12e4bf85104 100644 --- a/netbox_sys_plugin/views.py +++ b/netbox_sys_plugin/views.py @@ -29,3 +29,31 @@ class VmMaintenanceDeleteView(generic.ObjectDeleteView): """Vm maintenance delete view definition""" queryset = models.VirtualMachineMaintenance.objects.all() + + +class ProviderCredentialsView(generic.ObjectView): + """ Provider Credentials view definition""" + + queryset = ( + models.ProviderCredentials.objects.all() + ) + +class ProviderCredentialsListView(generic.ObjectListView): + """Cluster Provider Credentials list view definition""" + + queryset = models.ProviderCredentials.objects.all() + table = tables.ProviderCredentialsTable + filterset = filtersets.ProviderCredentialsFilterSet + filterset_form = forms.ProviderCredentialsFilterForm + +class ProviderCredentialsDeleteView(generic.ObjectDeleteView): + """Vm maintenance delete view definition""" + + queryset = models.ProviderCredentials.objects.all() + +class ProviderCredentialsEditView(generic.ObjectEditView): + """ + Defines the edit view for the Vm maintenance django model. + """ + queryset = models.ProviderCredentials.objects.all() + form = forms.ProviderCredentialsForm \ No newline at end of file