Browse Source

Introduced TopologyMap

Jeremy Stretch 9 years ago
parent
commit
29fd04026d

+ 2 - 2
netbox/dcim/api/urls.py

@@ -1,7 +1,7 @@
 from django.conf.urls import url
 
 from extras.models import GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
-from extras.api.views import GraphListView, TopologyMapperView
+from extras.api.views import GraphListView, TopologyMapView
 
 from .views import *
 
@@ -62,6 +62,6 @@ urlpatterns = [
 
     # Miscellaneous
     url(r'^related-connections/$', RelatedConnectionsView.as_view(), name='related_connections'),
-    url(r'^topology-mapper/$', TopologyMapperView.as_view(), name='topology_mapper'),
+    url(r'^topology-maps/(?P<slug>[\w-]+)/$', TopologyMapView.as_view(), name='topology_map'),
 
 ]

+ 3 - 0
netbox/dcim/views.py

@@ -15,6 +15,7 @@ from django.views.generic.edit import CreateView, UpdateView
 
 from ipam.models import Prefix, IPAddress, VLAN
 from circuits.models import Circuit
+from extras.models import TopologyMap
 from utilities.error_handlers import handle_protectederror
 from utilities.forms import ConfirmationForm
 from utilities.views import ObjectListView, BulkImportView, BulkEditView, BulkDeleteView
@@ -89,10 +90,12 @@ def site(request, slug):
         'vlan_count': VLAN.objects.filter(site=site).count(),
         'circuit_count': Circuit.objects.filter(site=site).count(),
     }
+    topology_maps = TopologyMap.objects.filter(site=site)
 
     return render(request, 'dcim/site.html', {
         'site': site,
         'stats': stats,
+        'topology_maps': topology_maps,
     })
 
 

+ 9 - 1
netbox/extras/admin.py

@@ -1,6 +1,6 @@
 from django.contrib import admin
 
-from .models import Graph, ExportTemplate
+from .models import Graph, ExportTemplate, TopologyMap
 
 
 @admin.register(Graph)
@@ -11,3 +11,11 @@ class GraphAdmin(admin.ModelAdmin):
 @admin.register(ExportTemplate)
 class ExportTemplateAdmin(admin.ModelAdmin):
     list_display = ['content_type', 'name', 'mime_type', 'file_extension']
+
+
+@admin.register(TopologyMap)
+class TopologyMapAdmin(admin.ModelAdmin):
+    list_display = ['name', 'slug', 'site']
+    prepopulated_fields = {
+        'slug': ['name'],
+    }

+ 7 - 8
netbox/extras/api/views.py

@@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404
 
 from circuits.models import Provider
 from dcim.models import Site, Device, Interface, InterfaceConnection
-from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_PROVIDER, GRAPH_TYPE_SITE
+from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_PROVIDER, GRAPH_TYPE_SITE
 from .serializers import GraphSerializer
 
 
@@ -38,24 +38,23 @@ class GraphListView(generics.ListAPIView):
         return queryset
 
 
-class TopologyMapperView(APIView):
+class TopologyMapView(APIView):
     """
     Generate a topology diagram
     """
 
-    def get(self, request):
+    def get(self, request, slug):
 
-        # Glean device sets to map. Each set is represented as a hierarchical tier in the diagram.
-        device_sets = request.GET.getlist('devices', [])
+        tmap = get_object_or_404(TopologyMap, slug=slug)
 
         # Construct the graph
         graph = pydot.Dot(graph_type='graph', ranksep='1')
-        for i, device_set in enumerate(device_sets):
+        for i, device_set in enumerate(tmap.device_sets):
 
             subgraph = pydot.Subgraph('sg{}'.format(i), rank='same')
 
             # Add a pseudonode for each device_set to enforce hierarchical layout
-            subgraph.add_node(pydot.Node('set{}'.format(i), shape='none'))
+            subgraph.add_node(pydot.Node('set{}'.format(i), shape='none', width='0', label=''))
             if i:
                 graph.add_edge(pydot.Edge('set{}'.format(i - 1), 'set{}'.format(i), style='invis'))
 
@@ -76,7 +75,7 @@ class TopologyMapperView(APIView):
 
         # Compile list of all devices
         device_superset = Q()
-        for regex in device_sets:
+        for regex in tmap.device_sets:
             device_superset = device_superset | Q(name__regex=regex)
 
         # Add all connections to the graph

+ 31 - 0
netbox/extras/migrations/0002_topologymap.py

@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.1 on 2016-04-08 18:53
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0005_auto_20160328_2135'),
+        ('extras', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='TopologyMap',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('device_patterns', models.TextField()),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='topology_maps', to='dcim.Site')),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+    ]

+ 23 - 0
netbox/extras/models.py

@@ -4,6 +4,9 @@ from django.http import HttpResponse
 from django.template import Template, Context
 
 
+from dcim.models import Site
+
+
 GRAPH_TYPE_INTERFACE = 100
 GRAPH_TYPE_PROVIDER = 200
 GRAPH_TYPE_SITE = 300
@@ -68,3 +71,23 @@ class ExportTemplate(models.Model):
             filename += '.{}'.format(self.file_extension)
         response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
         return response
+
+
+class TopologyMap(models.Model):
+    name = models.CharField(max_length=50, unique=True)
+    slug = models.SlugField(unique=True)
+    site = models.ForeignKey(Site, related_name='topology_maps', blank=True, null=True)
+    device_patterns = models.TextField()
+    description = models.CharField(max_length=100, blank=True)
+
+    class Meta:
+        ordering = ['name']
+
+    def __unicode__(self):
+        return self.name
+
+    @property
+    def device_sets(self):
+        if not self.device_patterns:
+            return None
+        return [line.strip() for line in self.device_patterns.split('\n')]

+ 19 - 0
netbox/templates/dcim/site.html

@@ -115,6 +115,25 @@
                 </tr>
             </table>
         </div>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Topology Maps</strong>
+            </div>
+            {% if topology_maps %}
+                <table class="table table-hover panel-body">
+                    {% for tm in topology_maps %}
+                        <tr>
+                            <td><a href="{% url 'dcim-api:topology_map' slug=tm.slug %}" target="_blank">{{ tm }}</a></td>
+                            <td>{{ tm.description }}</td>
+                        </tr>
+                    {% endfor %}
+                </table>
+            {% else %}
+                <div class="panel-body text-muted">
+                    None
+                </div>
+            {% endif %}
+        </div>
 	</div>
 </div>