2
0
Эх сурвалжийг харах

#20923: Convert vpn views to new UI layout

Jeremy Stretch 1 өдөр өмнө
parent
commit
d64c4d75f8

+ 3 - 0
netbox/templates/vpn/attrs/preshared_key.html

@@ -0,0 +1,3 @@
+{% load i18n %}
+<span id="secret" class="font-monospace" data-secret="{{ value }}">{{ value }}</span>
+<button type="button" class="btn btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>

+ 5 - 0
netbox/templates/vpn/attrs/proposals.html

@@ -0,0 +1,5 @@
+<ul class="list-unstyled mb-0">
+  {% for proposal in value.all %}
+    <li><a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a></li>
+  {% endfor %}
+</ul>

+ 0 - 62
netbox/templates/vpn/ikepolicy.html

@@ -1,63 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IKE Policy" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IKE Version" %}</th>
-            <td>{{ object.get_version_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Mode" %}</th>
-            <td>{{ object.get_mode_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Pre-Shared Key" %}</th>
-            <td>
-              <span id="secret" class="font-monospace" data-secret="{{ object.preshared_key }}">{{ object.preshared_key|placeholder }}</span>
-              {% if object.preshared_key %}
-                <button type="button" class="btn btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IPSec Profiles" %}</th>
-            <td>
-              <a href="{% url 'vpn:ipsecprofile_list' %}?ike_policy_id={{ object.pk }}">{{ object.ipsec_profiles.count }}</a>
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">{% trans "Proposals" %}</h2>
-        {% htmx_table 'vpn:ikeproposal_list' ike_policy_id=object.pk %}
-      </div>
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 61
netbox/templates/vpn/ikeproposal.html

@@ -1,62 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IKE Proposal" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Authentication method" %}</th>
-            <td>{{ object.get_authentication_method_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Encryption algorithm" %}</th>
-            <td>{{ object.get_encryption_algorithm_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Authentication algorithm" %}</th>
-            <td>{{ object.get_authentication_algorithm_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "DH group" %}</th>
-            <td>{{ object.get_group_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "SA lifetime (seconds)" %}</th>
-            <td>{{ object.sa_lifetime|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IKE Policies" %}</th>
-            <td>
-              <a href="{% url 'vpn:ikepolicy_list' %}?proposal_id={{ object.pk }}">{{ object.ike_policies.count }}</a>
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 50
netbox/templates/vpn/ipsecpolicy.html

@@ -1,51 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IPSec Policy" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "PFS group" %}</th>
-            <td>{{ object.get_pfs_group_display|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IPSec Profiles" %}</th>
-            <td>
-              <a href="{% url 'vpn:ipsecprofile_list' %}?ipsec_policy_id={{ object.pk }}">{{ object.ipsec_profiles.count }}</a>
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">{% trans "Proposals" %}</h2>
-        {% htmx_table 'vpn:ipsecproposal_list' ipsec_policy_id=object.pk %}
-      </div>
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 101
netbox/templates/vpn/ipsecprofile.html

@@ -1,102 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IPSec Profile" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Mode" %}</th>
-            <td>{{ object.get_mode_display }}</td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IKE Policy" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.ike_policy|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.ike_policy.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Version" %}</th>
-            <td>{{ object.ike_policy.get_version_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Mode" %}</th>
-            <td>{{ object.ike_policy.get_mode_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Proposals" %}</th>
-            <td>
-              <ul class="list-unstyled mb-0">
-                {% for proposal in object.ike_policy.proposals.all %}
-                  <li>
-                    <a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a>
-                  </li>
-                {% endfor %}
-              </ul>
-            </td>
-          </tr>
-        </table>
-      </div>
-      <div class="card">
-        <h2 class="card-header">{% trans "IPSec Policy" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.ipsec_policy|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.ipsec_policy.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Proposals" %}</th>
-            <td>
-              <ul class="list-unstyled mb-0">
-                {% for proposal in object.ipsec_policy.proposals.all %}
-                  <li>
-                    <a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a>
-                  </li>
-                {% endfor %}
-              </ul>
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "PFS Group" %}</th>
-            <td>{{ object.ipsec_policy.get_pfs_group_display }}</td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 57
netbox/templates/vpn/ipsecproposal.html

@@ -1,58 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "IPSec Proposal" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Encryption algorithm" %}</th>
-            <td>{{ object.get_encryption_algorithm_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Authentication algorithm" %}</th>
-            <td>{{ object.get_authentication_algorithm_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "SA lifetime (seconds)" %}</th>
-            <td>{{ object.sa_lifetime_seconds|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "SA lifetime (KB)" %}</th>
-            <td>{{ object.sa_lifetime_data|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IPSec Policies" %}</th>
-            <td>
-              <a href="{% url 'vpn:ipsecpolicy_list' %}?proposal_id={{ object.pk }}">{{ object.ipsec_policies.count }}</a>
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 77
netbox/templates/vpn/l2vpn.html

@@ -1,78 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
-{% load i18n %}
-
-{% block content %}
-<div class="row mb-3">
-	<div class="col col-12 col-md-6">
-    <div class="card">
-      <h2 class="card-header">{% trans "L2VPN Attributes" %}</h2>
-      <table class="table table-hover attr-table">
-        <tr>
-          <th scope="row">{% trans "Name" %}</th>
-          <td>{{ object.name|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Identifier" %}</th>
-          <td>{{ object.identifier|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Type" %}</th>
-          <td>{{ object.get_type_display }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Status" %}</th>
-          <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Description" %}</th>
-          <td>{{ object.description|placeholder }}</td>
-        </tr>
-        <tr>
-          <th scope="row">{% trans "Tenant" %}</th>
-          <td>{{ object.tenant|linkify|placeholder }}</td>
-        </tr>
-      </table>
-    </div>
-    {% include 'inc/panels/tags.html' with tags=object.tags.all url='vpn:l2vpn_list' %}
-    {% plugin_left_page object %}
-	</div>
-	<div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_right_page object %}
-    </div>
-</div>
-<div class="row mb-3">
-	<div class="col col-12 col-md-6">
-    {% include 'inc/panel_table.html' with table=import_targets_table heading="Import Route Targets" %}
-  </div>
-	<div class="col col-12 col-md-6">
-    {% include 'inc/panel_table.html' with table=export_targets_table heading="Export Route Targets" %}
-  </div>
-</div>
-<div class="row mb-3">
-	<div class="col col-md-12">
-    <div class="card">
-      <h2 class="card-header">
-        {% trans "Terminations" %}
-        {% if perms.vpn.add_l2vpntermination %}
-          <div class="card-actions">
-            <a href="{% url 'vpn:l2vpntermination_add' %}?l2vpn={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm{% if not object.can_add_termination %} disabled" aria-disabled="true{% endif %}">
-              <i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add a Termination" %}
-            </a>
-          </div>
-        {% endif %}
-      </h2>
-      {% htmx_table 'vpn:l2vpntermination_list' l2vpn_id=object.pk %}
-    </div>
-  </div>
-</div>
-<div class="row mb-3">
-  <div class="col col-md-12">
-    {% plugin_full_width_page object %}
-  </div>
-</div>
-{% endblock %}

+ 0 - 27
netbox/templates/vpn/l2vpntermination.html

@@ -1,28 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load i18n %}
-
-{% block content %}
-<div class="row">
-	<div class="col col-12 col-md-6">
-        <div class="card">
-            <h2 class="card-header">{% trans "L2VPN Attributes" %}</h2>
-            <table class="table table-hover">
-                <tr>
-                    <th scope="row">{% trans "L2VPN" %}</th>
-                    <td>{{ object.l2vpn|linkify }}</td>
-                </tr>
-                <tr>
-                    <th scope="row">{% trans "Assigned Object" %}</th>
-                    <td>{{ object.assigned_object|linkify }}</td>
-                </tr>
-            </table>
-        </div>
-	</div>
-	<div class="col col-12 col-md-6">
-        {% include 'inc/panels/custom_fields.html' %}
-        {% include 'inc/panels/tags.html' with tags=object.tags.all url='vpn:l2vpntermination_list' %}
-    </div>
-</div>
-
-{% endblock %}

+ 34 - 0
netbox/templates/vpn/panels/ipsecprofile_ike_policy.html

@@ -0,0 +1,34 @@
+{% load helpers %}
+{% load i18n %}
+
+<div class="card">
+  <h2 class="card-header">{% trans "IKE Policy" %}</h2>
+  <table class="table table-hover attr-table">
+    <tr>
+      <th scope="row">{% trans "Name" %}</th>
+      <td>{{ object.ike_policy|linkify }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Description" %}</th>
+      <td>{{ object.ike_policy.description|placeholder }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Version" %}</th>
+      <td>{{ object.ike_policy.get_version_display }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Mode" %}</th>
+      <td>{{ object.ike_policy.get_mode_display }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Proposals" %}</th>
+      <td>
+        <ul class="list-unstyled mb-0">
+          {% for proposal in object.ike_policy.proposals.all %}
+            <li><a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a></li>
+          {% endfor %}
+        </ul>
+      </td>
+    </tr>
+  </table>
+</div>

+ 30 - 0
netbox/templates/vpn/panels/ipsecprofile_ipsec_policy.html

@@ -0,0 +1,30 @@
+{% load helpers %}
+{% load i18n %}
+
+<div class="card">
+  <h2 class="card-header">{% trans "IPSec Policy" %}</h2>
+  <table class="table table-hover attr-table">
+    <tr>
+      <th scope="row">{% trans "Name" %}</th>
+      <td>{{ object.ipsec_policy|linkify }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Description" %}</th>
+      <td>{{ object.ipsec_policy.description|placeholder }}</td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "Proposals" %}</th>
+      <td>
+        <ul class="list-unstyled mb-0">
+          {% for proposal in object.ipsec_policy.proposals.all %}
+            <li><a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a></li>
+          {% endfor %}
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <th scope="row">{% trans "PFS Group" %}</th>
+      <td>{{ object.ipsec_policy.get_pfs_group_display }}</td>
+    </tr>
+  </table>
+</div>

+ 0 - 76
netbox/templates/vpn/tunnel.html

@@ -1,6 +1,4 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
 {% load i18n %}
 
 {% block extra_controls %}
@@ -10,77 +8,3 @@
     </a>
   {% endif %}
 {% endblock %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Tunnel" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Status" %}</th>
-            <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Group" %}</th>
-            <td>{{ object.group|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Encapsulation" %}</th>
-            <td>{{ object.get_encapsulation_display }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "IPSec profile" %}</th>
-            <td>{{ object.ipsec_profile|linkify|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Tunnel ID" %}</th>
-            <td>{{ object.tunnel_id|placeholder }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Tenant" %}</th>
-            <td>
-              {% if object.tenant.group %}
-                {{ object.tenant.group|linkify }} /
-              {% endif %}
-              {{ object.tenant|linkify|placeholder }}
-            </td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">
-          {% trans "Terminations" %}
-          {% if perms.vpn.add_tunneltermination %}
-            <div class="card-actions">
-              <a href="{% url 'vpn:tunneltermination_add' %}?tunnel={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
-                <i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add a Termination" %}
-              </a>
-            </div>
-          {% endif %}
-        </h2>
-        {% htmx_table 'vpn:tunneltermination_list' tunnel_id=object.pk %}
-      </div>
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 40
netbox/templates/vpn/tunnelgroup.html

@@ -1,13 +1,6 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load render_table from django_tables2 %}
 {% load i18n %}
 
-{% block breadcrumbs %}
-  <li class="breadcrumb-item"><a href="{% url 'vpn:tunnelgroup_list' %}">{% trans "Tunnel Groups" %}</a></li>
-{% endblock %}
-
 {% block extra_controls %}
   {% if perms.vpn.add_tunnel %}
     <a href="{% url 'vpn:tunnel_add' %}?group={{ object.pk }}" class="btn btn-primary">
@@ -15,36 +8,3 @@
     </a>
   {% endif %}
 {% endblock extra_controls %}
-
-{% block content %}
-  <div class="row mb-3">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Tunnel Group" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Name" %}</th>
-            <td>{{ object.name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Description" %}</th>
-            <td>{{ object.description|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/related_objects.html' %}
-      {% include 'inc/panels/comments.html' %}
-      {% include 'inc/panels/custom_fields.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 56
netbox/templates/vpn/tunneltermination.html

@@ -1,57 +1 @@
 {% extends 'generic/object.html' %}
-{% load helpers %}
-{% load plugins %}
-{% load i18n %}
-
-{% block content %}
-  <div class="row">
-    <div class="col col-12 col-md-6">
-      <div class="card">
-        <h2 class="card-header">{% trans "Tunnel Termination" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "Tunnel" %}</th>
-            <td>{{ object.tunnel|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Role" %}</th>
-            <td>{% badge object.get_role_display bg_color=object.get_role_color %}</td>
-          </tr>
-          <tr>
-            <th scope="row">
-              {% if object.termination.device %}
-                {% trans "Device" %}
-              {% elif object.termination.virtual_machine %}
-                {% trans "Virtual Machine" %}
-              {% endif %}
-            </th>
-            <td>{{ object.termination.parent_object|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Interface" %}</th>
-            <td>{{ object.termination|linkify }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Outside IP" %}</th>
-            <td>{{ object.outside_ip|linkify|placeholder }}</td>
-          </tr>
-        </table>
-      </div>
-      {% plugin_left_page object %}
-    </div>
-    <div class="col col-12 col-md-6">
-      {% include 'inc/panels/custom_fields.html' %}
-      {% include 'inc/panels/tags.html' %}
-      {% plugin_right_page object %}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">{% trans "Peer Terminations" %}</h2>
-        {% htmx_table 'vpn:tunneltermination_list' tunnel_id=object.tunnel.pk id__n=object.pk %}
-      </div>
-      {% plugin_full_width_page object %}
-    </div>
-  </div>
-{% endblock %}

+ 0 - 0
netbox/vpn/ui/__init__.py


+ 85 - 0
netbox/vpn/ui/panels.py

@@ -0,0 +1,85 @@
+from django.utils.translation import gettext_lazy as _
+
+from netbox.ui import attrs, panels
+
+
+class TunnelGroupPanel(panels.OrganizationalObjectPanel):
+    pass
+
+
+class TunnelPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    status = attrs.ChoiceAttr('status')
+    group = attrs.RelatedObjectAttr('group', linkify=True)
+    description = attrs.TextAttr('description')
+    encapsulation = attrs.ChoiceAttr('encapsulation')
+    ipsec_profile = attrs.RelatedObjectAttr('ipsec_profile', linkify=True, label=_('IPSec profile'))
+    tunnel_id = attrs.TextAttr('tunnel_id', label=_('Tunnel ID'))
+    tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
+
+
+class TunnelTerminationPanel(panels.ObjectAttributesPanel):
+    tunnel = attrs.RelatedObjectAttr('tunnel', linkify=True)
+    role = attrs.ChoiceAttr('role')
+    parent_object = attrs.RelatedObjectAttr(
+        'termination.parent_object', linkify=True, label=_('Parent')
+    )
+    termination = attrs.RelatedObjectAttr('termination', linkify=True, label=_('Interface'))
+    outside_ip = attrs.RelatedObjectAttr('outside_ip', linkify=True, label=_('Outside IP'))
+
+
+class IKEProposalPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    description = attrs.TextAttr('description')
+    authentication_method = attrs.ChoiceAttr('authentication_method', label=_('Authentication method'))
+    encryption_algorithm = attrs.ChoiceAttr('encryption_algorithm', label=_('Encryption algorithm'))
+    authentication_algorithm = attrs.ChoiceAttr('authentication_algorithm', label=_('Authentication algorithm'))
+    group = attrs.ChoiceAttr('group', label=_('DH group'))
+    sa_lifetime = attrs.TextAttr('sa_lifetime', label=_('SA lifetime (seconds)'))
+
+
+class IKEPolicyPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    description = attrs.TextAttr('description')
+    version = attrs.ChoiceAttr('version', label=_('IKE version'))
+    mode = attrs.ChoiceAttr('mode')
+    preshared_key = attrs.TemplatedAttr(
+        'preshared_key',
+        label=_('Pre-shared key'),
+        template_name='vpn/attrs/preshared_key.html',
+    )
+
+
+class IPSecProposalPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    description = attrs.TextAttr('description')
+    encryption_algorithm = attrs.ChoiceAttr('encryption_algorithm', label=_('Encryption algorithm'))
+    authentication_algorithm = attrs.ChoiceAttr('authentication_algorithm', label=_('Authentication algorithm'))
+    sa_lifetime_seconds = attrs.TextAttr('sa_lifetime_seconds', label=_('SA lifetime (seconds)'))
+    sa_lifetime_data = attrs.TextAttr('sa_lifetime_data', label=_('SA lifetime (KB)'))
+
+
+class IPSecPolicyPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    description = attrs.TextAttr('description')
+    pfs_group = attrs.ChoiceAttr('pfs_group', label=_('PFS group'))
+
+
+class IPSecProfilePanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    description = attrs.TextAttr('description')
+    mode = attrs.ChoiceAttr('mode')
+
+
+class L2VPNPanel(panels.ObjectAttributesPanel):
+    name = attrs.TextAttr('name')
+    identifier = attrs.TextAttr('identifier')
+    type = attrs.ChoiceAttr('type')
+    status = attrs.ChoiceAttr('status')
+    description = attrs.TextAttr('description')
+    tenant = attrs.RelatedObjectAttr('tenant', linkify=True)
+
+
+class L2VPNTerminationPanel(panels.ObjectAttributesPanel):
+    l2vpn = attrs.RelatedObjectAttr('l2vpn', linkify=True, label=_('L2VPN'))
+    assigned_object = attrs.RelatedObjectAttr('assigned_object', linkify=True, label=_('Assigned object'))

+ 209 - 2
netbox/vpn/views.py

@@ -1,11 +1,24 @@
+from django.utils.translation import gettext_lazy as _
+
+from extras.ui.panels import CustomFieldsPanel, TagsPanel
 from ipam.tables import RouteTargetTable
 from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
+from netbox.ui import actions, layout
+from netbox.ui.panels import (
+    CommentsPanel,
+    ContextTablePanel,
+    ObjectsTablePanel,
+    PluginContentPanel,
+    RelatedObjectsPanel,
+    TemplatePanel,
+)
 from netbox.views import generic
 from utilities.query import count_related
 from utilities.views import GetRelatedModelsMixin, register_model_view
 
 from . import filtersets, forms, tables
 from .models import *
+from .ui import panels
 
 #
 # Tunnel groups
@@ -25,6 +38,17 @@ class TunnelGroupListView(generic.ObjectListView):
 @register_model_view(TunnelGroup)
 class TunnelGroupView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = TunnelGroup.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.TunnelGroupPanel(),
+            TagsPanel(),
+        ],
+        right_panels=[
+            RelatedObjectsPanel(),
+            CommentsPanel(),
+            CustomFieldsPanel(),
+        ],
+    )
 
     def get_extra_context(self, request, instance):
         return {
@@ -92,6 +116,30 @@ class TunnelListView(generic.ObjectListView):
 @register_model_view(Tunnel)
 class TunnelView(generic.ObjectView):
     queryset = Tunnel.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.TunnelPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            TagsPanel(),
+            CommentsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.tunneltermination',
+                filters={'tunnel_id': lambda ctx: ctx['object'].pk},
+                actions=[
+                    actions.AddObject(
+                        'vpn.tunneltermination',
+                        url_params={'tunnel': lambda ctx: ctx['object'].pk},
+                        label=_('Add a Termination'),
+                    ),
+                ],
+                title=_('Terminations'),
+            ),
+        ],
+    )
 
 
 @register_model_view(Tunnel, 'add', detail=False)
@@ -160,6 +208,25 @@ class TunnelTerminationListView(generic.ObjectListView):
 @register_model_view(TunnelTermination)
 class TunnelTerminationView(generic.ObjectView):
     queryset = TunnelTermination.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.TunnelTerminationPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.tunneltermination',
+                filters={
+                    'tunnel_id': lambda ctx: ctx['object'].tunnel.pk,
+                    'id__n': lambda ctx: ctx['object'].pk,
+                },
+                title=_('Peer Terminations'),
+            ),
+        ],
+    )
 
 
 @register_model_view(TunnelTermination, 'add', detail=False)
@@ -210,6 +277,23 @@ class IKEProposalListView(generic.ObjectListView):
 @register_model_view(IKEProposal)
 class IKEProposalView(generic.ObjectView):
     queryset = IKEProposal.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.IKEProposalPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            CommentsPanel(),
+            TagsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.ikepolicy',
+                filters={'ike_proposal_id': lambda ctx: ctx['object'].pk},
+                title=_('IKE Policies'),
+            ),
+        ],
+    )
 
 
 @register_model_view(IKEProposal, 'add', detail=False)
@@ -264,8 +348,31 @@ class IKEPolicyListView(generic.ObjectListView):
 
 
 @register_model_view(IKEPolicy)
-class IKEPolicyView(generic.ObjectView):
+class IKEPolicyView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = IKEPolicy.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.IKEPolicyPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            CommentsPanel(),
+            TagsPanel(),
+            RelatedObjectsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.ikeproposal',
+                filters={'ike_policy_id': lambda ctx: ctx['object'].pk},
+                title=_('Proposals'),
+            ),
+        ],
+    )
+
+    def get_extra_context(self, request, instance):
+        return {
+            'related_models': self.get_related_models(request, instance),
+        }
 
 
 @register_model_view(IKEPolicy, 'add', detail=False)
@@ -322,6 +429,23 @@ class IPSecProposalListView(generic.ObjectListView):
 @register_model_view(IPSecProposal)
 class IPSecProposalView(generic.ObjectView):
     queryset = IPSecProposal.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.IPSecProposalPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            CommentsPanel(),
+            TagsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.ipsecpolicy',
+                filters={'ipsec_proposal_id': lambda ctx: ctx['object'].pk},
+                title=_('IPSec Policies'),
+            ),
+        ],
+    )
 
 
 @register_model_view(IPSecProposal, 'add', detail=False)
@@ -376,8 +500,31 @@ class IPSecPolicyListView(generic.ObjectListView):
 
 
 @register_model_view(IPSecPolicy)
-class IPSecPolicyView(generic.ObjectView):
+class IPSecPolicyView(GetRelatedModelsMixin, generic.ObjectView):
     queryset = IPSecPolicy.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.IPSecPolicyPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            CommentsPanel(),
+            TagsPanel(),
+            RelatedObjectsPanel(),
+        ],
+        bottom_panels=[
+            ObjectsTablePanel(
+                'vpn.ipsecproposal',
+                filters={'ipsec_policy_id': lambda ctx: ctx['object'].pk},
+                title=_('Proposals'),
+            ),
+        ],
+    )
+
+    def get_extra_context(self, request, instance):
+        return {
+            'related_models': self.get_related_models(request, instance),
+        }
 
 
 @register_model_view(IPSecPolicy, 'add', detail=False)
@@ -434,6 +581,18 @@ class IPSecProfileListView(generic.ObjectListView):
 @register_model_view(IPSecProfile)
 class IPSecProfileView(generic.ObjectView):
     queryset = IPSecProfile.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.IPSecProfilePanel(),
+            TagsPanel(),
+            CustomFieldsPanel(),
+            CommentsPanel(),
+        ],
+        right_panels=[
+            TemplatePanel('vpn/panels/ipsecprofile_ike_policy.html'),
+            TemplatePanel('vpn/panels/ipsecprofile_ipsec_policy.html'),
+        ],
+    )
 
 
 @register_model_view(IPSecProfile, 'add', detail=False)
@@ -490,6 +649,45 @@ class L2VPNListView(generic.ObjectListView):
 @register_model_view(L2VPN)
 class L2VPNView(generic.ObjectView):
     queryset = L2VPN.objects.all()
+    layout = layout.Layout(
+        layout.Row(
+            layout.Column(
+                panels.L2VPNPanel(),
+                TagsPanel(),
+                PluginContentPanel('left_page'),
+            ),
+            layout.Column(
+                CustomFieldsPanel(),
+                CommentsPanel(),
+                PluginContentPanel('right_page'),
+            ),
+        ),
+        layout.Row(
+            layout.Column(
+                ContextTablePanel('import_targets_table', title=_('Import Route Targets')),
+            ),
+            layout.Column(
+                ContextTablePanel('export_targets_table', title=_('Export Route Targets')),
+            ),
+        ),
+        layout.Row(
+            layout.Column(
+                ObjectsTablePanel(
+                    'vpn.l2vpntermination',
+                    filters={'l2vpn_id': lambda ctx: ctx['object'].pk},
+                    actions=[
+                        actions.AddObject(
+                            'vpn.l2vpntermination',
+                            url_params={'l2vpn': lambda ctx: ctx['object'].pk},
+                            label=_('Add a Termination'),
+                        ),
+                    ],
+                    title=_('Terminations'),
+                ),
+                PluginContentPanel('full_width_page'),
+            ),
+        ),
+    )
 
     def get_extra_context(self, request, instance):
         import_targets_table = RouteTargetTable(
@@ -564,6 +762,15 @@ class L2VPNTerminationListView(generic.ObjectListView):
 @register_model_view(L2VPNTermination)
 class L2VPNTerminationView(generic.ObjectView):
     queryset = L2VPNTermination.objects.all()
+    layout = layout.SimpleLayout(
+        left_panels=[
+            panels.L2VPNTerminationPanel(),
+        ],
+        right_panels=[
+            CustomFieldsPanel(),
+            TagsPanel(),
+        ],
+    )
 
 
 @register_model_view(L2VPNTermination, 'add', detail=False)