Jeremy Stretch 6 месяцев назад
Родитель
Сommit
8238fda8ad
3 измененных файлов с 146 добавлено и 67 удалено
  1. 6 0
      netbox/core/tests/test_views.py
  2. 19 5
      netbox/core/views.py
  3. 121 62
      netbox/templates/core/system.html

+ 6 - 0
netbox/core/tests/test_views.py

@@ -1,3 +1,4 @@
+import json
 import urllib.parse
 import uuid
 from datetime import datetime
@@ -366,6 +367,11 @@ class SystemTestCase(TestCase):
         # Test export
         response = self.client.get(f"{reverse('core:system')}?export=true")
         self.assertEqual(response.status_code, 200)
+        data = json.loads(response.content)
+        self.assertIn('netbox_release', data)
+        self.assertIn('plugins', data)
+        self.assertIn('config', data)
+        self.assertIn('objects', data)
 
     def test_system_view_with_config_revision(self):
         ConfigRevision.objects.create()

+ 19 - 5
netbox/core/views.py

@@ -1,7 +1,7 @@
 import json
 import platform
 
-from django import __version__ as DJANGO_VERSION
+from django import __version__ as django_version
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.mixins import UserPassesTestMixin
@@ -23,7 +23,7 @@ from rq.worker_registration import clean_worker_registry
 from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs_from_status, requeue_rq_job, stop_rq_job
 from netbox.config import get_config, PARAMS
 from netbox.object_actions import AddObject, BulkDelete, BulkExport, DeleteObject
-from netbox.registry import registry
+from netbox.plugins.utils import get_installed_plugins
 from netbox.views import generic
 from netbox.views.generic.base import BaseObjectView
 from netbox.views.generic.mixins import TableMixin
@@ -546,7 +546,7 @@ class SystemView(UserPassesTestMixin, View):
 
     def get(self, request):
 
-        # System stats
+        # System status
         psql_version = db_name = db_size = None
         try:
             with connection.cursor() as cursor:
@@ -561,7 +561,7 @@ class SystemView(UserPassesTestMixin, View):
             pass
         stats = {
             'netbox_release': settings.RELEASE,
-            'django_version': DJANGO_VERSION,
+            'django_version': django_version,
             'python_version': platform.python_version(),
             'postgresql_version': psql_version,
             'database_name': db_name,
@@ -572,16 +572,28 @@ class SystemView(UserPassesTestMixin, View):
         # Configuration
         config = get_config()
 
+        # Plugins
+        plugins = get_installed_plugins()
+
+        # Object counts
+        objects = {}
+        for ot in ObjectType.objects.public().order_by('app_label', 'model'):
+            if model := ot.model_class():
+                objects[ot] = model.objects.count()
+
         # Raw data export
         if 'export' in request.GET:
             stats['netbox_release'] = stats['netbox_release'].asdict()
             params = [param.name for param in PARAMS]
             data = {
                 **stats,
-                'plugins': registry['plugins']['installed'],
+                'plugins': plugins,
                 'config': {
                     k: getattr(config, k) for k in sorted(params)
                 },
+                'objects': {
+                    f'{ot.app_label}.{ot.model}': count for ot, count in objects.items()
+                },
             }
             response = HttpResponse(json.dumps(data, cls=ConfigJSONEncoder, indent=4), content_type='text/json')
             response['Content-Disposition'] = 'attachment; filename="netbox.json"'
@@ -595,6 +607,8 @@ class SystemView(UserPassesTestMixin, View):
         return render(request, 'core/system.html', {
             'stats': stats,
             'config': config,
+            'plugins': plugins,
+            'objects': objects,
         })
 
 

+ 121 - 62
netbox/templates/core/system.html

@@ -8,82 +8,141 @@
 
 {% block controls %}
   <a href="?export=true" class="btn btn-purple">
-    <i class="mdi mdi-download"></i> {% trans "Export" %}
+    <i class="mdi mdi-download"></i> {% trans "Export All" %}
   </a>
 {% endblock controls %}
 
 {% block tabs %}
-  <ul class="nav nav-tabs px-3">
+  <ul class="nav nav-tabs" role="tablist">
     <li class="nav-item" role="presentation">
-      <a class="nav-link active" role="tab">{% trans "Status" %}</a>
+      <a class="nav-link active" id="status-tab" data-bs-toggle="tab" data-bs-target="#status-panel" type="button" role="tab" aria-selected="true">
+        {% trans "Status" %}
+      </a>
+    </li>
+    <li class="nav-item" role="presentation">
+      <a class="nav-link" id="config-tab" data-bs-toggle="tab" data-bs-target="#config-panel" type="button" role="tab">
+        {% trans "Config" %}
+      </a>
+    </li>
+    <li class="nav-item" role="presentation">
+      <a class="nav-link" id="plugins-tab" data-bs-toggle="tab" data-bs-target="#plugins-panel" type="button" role="tab">
+        {% trans "Plugins" %}
+      </a>
+    </li>
+    <li class="nav-item" role="presentation">
+      <a class="nav-link" id="objects-tab" data-bs-toggle="tab" data-bs-target="#objects-panel" type="button" role="tab">
+        {% trans "Object Counts" %}
+      </a>
     </li>
   </ul>
 {% endblock tabs %}
 
 {% block content %}
-  {# System status #}
-  <div class="row mb-3">
-    <div class="col">
-      <div class="card">
-        <h2 class="card-header">{% trans "System Status" %}</h2>
-        <table class="table table-hover attr-table">
-          <tr>
-            <th scope="row">{% trans "NetBox release" %}</th>
-            <td>
-              {{ stats.netbox_release.name }}
-              {% if stats.netbox_release.published %}
-               ({{ stats.netbox_release.published|isodate }})
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Python version" %}</th>
-            <td>{{ stats.python_version }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Django version" %}</th>
-            <td>{{ stats.django_version }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "PostgreSQL version" %}</th>
-            <td>{{ stats.postgresql_version }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Database name" %}</th>
-            <td>{{ stats.database_name }}</td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "Database size" %}</th>
-            <td>
-              {% if stats.database_size %}
-                {{ stats.database_size }}
-              {% else %}
-                <span class="text-muted">{% trans "Unavailable" %}</span>
-              {% endif %}
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "RQ workers" %}</th>
-            <td>
-              <a href="{% url 'core:background_queue_list' %}">{{ stats.rq_worker_count }}</a>
-              ({% trans "default queue" %})
-            </td>
-          </tr>
-          <tr>
-            <th scope="row">{% trans "System time" %}</th>
-            <td>{% now 'Y-m-d H:i:s T' %}</td>
-          </tr>
-        </table>
+  {# Status panel #}
+  <div class="tab-pane show active" id="status-panel" role="tabpanel" aria-labelledby="status-tab">
+    <div class="row mb-3">
+      <div class="col">
+        <div class="card">
+          <h2 class="card-header">{% trans "System Status" %}</h2>
+          <table class="table table-hover attr-table">
+            <tr>
+              <th scope="row">{% trans "NetBox release" %}</th>
+              <td>
+                {{ stats.netbox_release.name }}
+                {% if stats.netbox_release.published %}
+                 ({{ stats.netbox_release.published|isodate }})
+                {% endif %}
+              </td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "Python version" %}</th>
+              <td>{{ stats.python_version }}</td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "Django version" %}</th>
+              <td>{{ stats.django_version }}</td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "PostgreSQL version" %}</th>
+              <td>{{ stats.postgresql_version }}</td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "Database name" %}</th>
+              <td>{{ stats.database_name }}</td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "Database size" %}</th>
+              <td>
+                {% if stats.database_size %}
+                  {{ stats.database_size }}
+                {% else %}
+                  <span class="text-muted">{% trans "Unavailable" %}</span>
+                {% endif %}
+              </td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "RQ workers" %}</th>
+              <td>
+                <a href="{% url 'core:background_queue_list' %}">{{ stats.rq_worker_count }}</a>
+                ({% trans "default queue" %})
+              </td>
+            </tr>
+            <tr>
+              <th scope="row">{% trans "System time" %}</th>
+              <td>{% now 'Y-m-d H:i:s T' %}</td>
+            </tr>
+          </table>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  {# Config panel #}
+  <div class="tab-pane" id="config-panel" role="tabpanel" aria-labelledby="config-tab">
+    <div class="row mb-3">
+      <div class="col">
+        <div class="card">
+          <h2 class="card-header">{% trans "Current Configuration" %}</h2>
+          {% include 'core/inc/config_data.html' %}
+        </div>
+      </div>
+    </div>
+  </div>
+
+  {# Plugins panel #}
+  <div class="tab-pane" id="plugins-panel" role="tabpanel" aria-labelledby="plugins-tab">
+    <div class="row mb-3">
+      <div class="col">
+        <div class="card">
+          <h2 class="card-header">{% trans "Installed Plugins" %}</h2>
+          <table class="table table-hover attr-table">
+            {% for plugin, version in plugins.items %}
+              <tr>
+                <td>{{ plugin }}</td>
+                <td>{{ version }}</td>
+              </tr>
+            {% endfor %}
+          </table>
+        </div>
       </div>
     </div>
   </div>
 
-  {# Configuration #}
-  <div class="row mb-3">
-    <div class="col col-md-12">
-      <div class="card">
-        <h2 class="card-header">{% trans "Current Configuration" %}</h2>
-        {% include 'core/inc/config_data.html' %}
+  {# Objects panel #}
+  <div class="tab-pane" id="objects-panel" role="tabpanel" aria-labelledby="objects-tab">
+    <div class="row mb-3">
+      <div class="col col-md-12">
+        <div class="card">
+          <h2 class="card-header">{% trans "Object Counts" %}</h2>
+          <table class="table table-hover attr-table">
+            {% for object_type, count in objects.items %}
+              <tr{% if not count %} class="text-muted"{% endif %}>
+                <td>{{ object_type }}</td>
+                <td>{{ count }}</td>
+              </tr>
+            {% endfor %}
+          </table>
+        </div>
       </div>
     </div>
   </div>