|
@@ -1,10 +1,15 @@
|
|
|
+import pydot
|
|
|
from rest_framework import generics
|
|
|
+from rest_framework.views import APIView
|
|
|
+import tempfile
|
|
|
+from wsgiref.util import FileWrapper
|
|
|
|
|
|
-from django.http import Http404
|
|
|
+from django.db.models import Q
|
|
|
+from django.http import Http404, HttpResponse
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
|
|
from circuits.models import Provider
|
|
|
-from dcim.models import Site, Interface
|
|
|
+from dcim.models import Site, Device, Interface, InterfaceConnection
|
|
|
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_PROVIDER, GRAPH_TYPE_SITE
|
|
|
from .serializers import GraphSerializer
|
|
|
|
|
@@ -31,3 +36,60 @@ class GraphListView(generics.ListAPIView):
|
|
|
raise Http404()
|
|
|
queryset = Graph.objects.filter(type=graph_type)
|
|
|
return queryset
|
|
|
+
|
|
|
+
|
|
|
+class TopologyMapperView(APIView):
|
|
|
+ """
|
|
|
+ Generate a topology diagram
|
|
|
+ """
|
|
|
+
|
|
|
+ def get(self, request):
|
|
|
+
|
|
|
+ # Glean device sets to map. Each set is represented as a hierarchical tier in the diagram.
|
|
|
+ device_sets = request.GET.getlist('devices', [])
|
|
|
+
|
|
|
+ # Construct the graph
|
|
|
+ graph = pydot.Dot(graph_type='graph', ranksep='1')
|
|
|
+ for i, device_set in enumerate(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'))
|
|
|
+ if i:
|
|
|
+ graph.add_edge(pydot.Edge('set{}'.format(i - 1), 'set{}'.format(i), style='invis'))
|
|
|
+
|
|
|
+ # Add each device to the graph
|
|
|
+ devices = Device.objects.filter(name__regex=device_set)
|
|
|
+ for d in devices:
|
|
|
+ node = pydot.Node(d.name)
|
|
|
+ subgraph.add_node(node)
|
|
|
+
|
|
|
+ # Add an invisible connection to each successive device in a set to enforce horizontal order
|
|
|
+ for j in range(0, len(devices) - 1):
|
|
|
+ edge = pydot.Edge(devices[j].name, devices[j + 1].name)
|
|
|
+ # edge.set('style', 'invis') doesn't seem to work for some reason
|
|
|
+ edge.set_style('invis')
|
|
|
+ subgraph.add_edge(edge)
|
|
|
+
|
|
|
+ graph.add_subgraph(subgraph)
|
|
|
+
|
|
|
+ # Compile list of all devices
|
|
|
+ device_superset = Q()
|
|
|
+ for regex in device_sets:
|
|
|
+ device_superset = device_superset | Q(name__regex=regex)
|
|
|
+
|
|
|
+ # Add all connections to the graph
|
|
|
+ devices = Device.objects.filter(*(device_superset,))
|
|
|
+ connections = InterfaceConnection.objects.filter(interface_a__device__in=devices, interface_b__device__in=devices)
|
|
|
+ for c in connections:
|
|
|
+ edge = pydot.Edge(c.interface_a.device.name, c.interface_b.device.name)
|
|
|
+ graph.add_edge(edge)
|
|
|
+
|
|
|
+ # Write the image to disk and return
|
|
|
+ topo_file = tempfile.NamedTemporaryFile()
|
|
|
+ graph.write(topo_file.name, format='png')
|
|
|
+ response = HttpResponse(FileWrapper(topo_file), content_type='image/png')
|
|
|
+ topo_file.close()
|
|
|
+
|
|
|
+ return response
|