Skip to content
This repository was archived by the owner on Jun 27, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions django_netjsongraph/api/generics.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import json

from django.utils.translation import ugettext_lazy as _
from netdiff.exceptions import NetdiffException
from rest_framework import generics
Expand Down Expand Up @@ -25,6 +27,28 @@ class BaseNetworkGraphView(generics.RetrieveAPIView):
serializer_class = NetworkGraphSerializer


class BaseNetworkGraphHistoryView(APIView):
"""
History of a specific topology returned
in NetJSON NetworkGraph format
"""

def get(self, request, pk, format=None):
topology = get_object_or_404(self.topology_model, pk)
date = request.query_params.get('date')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write a test in which you supply a rubbish date here, make it fail and then find a fix (great occasion to learn TDD)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I get it 😄

options = dict(topology=topology, date=date)
# missing date: 400
if not date:
return Response({'detail': _('missing required "date" parameter')},
status=400)
try:
s = self.snapshot_model.objects.get(**options)
return Response(json.loads(s.data))
except:
return Response({'detail': _('wrong date')},
status=403)


class BaseReceiveTopologyView(APIView):
"""
This views allow nodes to send topology data using the RECEIVE strategy.
Expand Down
12 changes: 9 additions & 3 deletions django_netjsongraph/api/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ..models import Topology
from .generics import (BaseNetworkCollectionView, BaseNetworkGraphView,
BaseReceiveTopologyView)
from ..models import Snapshot, Topology
from .generics import (BaseNetworkCollectionView, BaseNetworkGraphHistoryView,
BaseNetworkGraphView, BaseReceiveTopologyView)


class NetworkCollectionView(BaseNetworkCollectionView):
Expand All @@ -15,6 +15,12 @@ class ReceiveTopologyView(BaseReceiveTopologyView):
model = Topology


class NetworkGraphHistoryView(BaseNetworkGraphHistoryView):
topology_model = Topology
snapshot_model = Snapshot


network_collection = NetworkCollectionView.as_view()
network_graph = NetworkGraphView.as_view()
network_graph_history = NetworkGraphHistoryView.as_view()
receive_topology = ReceiveTopologyView.as_view()
33 changes: 29 additions & 4 deletions django_netjsongraph/base/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from django.utils.translation import ugettext_lazy as _

from ..contextmanagers import log_failure
from ..utils import get_object_or_404


class TimeStampedEditableAdmin(ModelAdmin):
Expand All @@ -25,9 +24,12 @@ class BaseAdmin(TimeStampedEditableAdmin):

class Media:
css = {'all': [static('netjsongraph/css/src/netjsongraph.css'),
static('netjsongraph/css/lib/jquery-ui.min.css'),
static('netjsongraph/css/style.css'),
static('netjsongraph/css/admin.css')]}
js = [static('netjsongraph/js/lib/d3.min.js'),
static('netjsongraph/js/lib/jquery.min.js'),
static('netjsongraph/js/lib/jquery-ui.min.js'),
static('netjsongraph/js/src/netjsongraph.js'),
static('netjsongraph/js/receive-url.js'),
static('netjsongraph/js/strategy-switcher.js'),
Expand Down Expand Up @@ -83,7 +85,10 @@ def get_urls(self):
return [
url(r'^visualize/(?P<pk>[^/]+)/$',
self.admin_site.admin_view(self.visualize_view),
name='{0}_visualize'.format(url_prefix))
name='{0}_visualize'.format(url_prefix)),
url(r'^visualize/history/(?P<pk>[^/]+)/$',
self.admin_site.admin_view(self.visualize_history_view),
name='{0}_visualize_history'.format(url_prefix))
] + super(AbstractTopologyAdmin, self).get_urls()

def _message(self, request, rows, suffix, level=messages.SUCCESS):
Expand Down Expand Up @@ -123,15 +128,35 @@ def unpublish_selected(self, request, queryset):
unpublish_selected.short_description = _('Unpublish selected items')

def visualize_view(self, request, pk):
topology = get_object_or_404(self.model, pk)
api_url = reverse('network_graph', args=[pk])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this change 👍

context = self.admin_site.each_context(request)
opts = self.model._meta
prefix = 'admin:{0}_{1}'.format(self.opts.app_label, self.model.__name__.lower())
graph_url = reverse('{0}_visualize_history'.format(prefix), args=[pk])
context.update({
'is_popup': True,
'opts': opts,
'change': False,
'media': self.media,
'api_url': api_url,
'graph_url': graph_url
})
return TemplateResponse(request, 'admin/%s/visualize.html' % opts.app_label, context)

def visualize_history_view(self, request, pk):
date = request.GET.get('date', '')
api_url = '{0}?date={1}'.format(reverse('network_graph_history', args=[pk]), date)
context = self.admin_site.each_context(request)
opts = self.model._meta
prefix = 'admin:{0}_{1}'.format(self.opts.app_label, self.model.__name__.lower())
graph_url = reverse('{0}_visualize_history'.format(prefix), args=[pk])
context.update({
'is_popup': True,
'opts': opts,
'change': False,
'media': self.media,
'topology': topology
'api_url': api_url,
'graph_url': graph_url
})
return TemplateResponse(request, 'admin/%s/visualize.html' % opts.app_label, context)

Expand Down
20 changes: 20 additions & 0 deletions django_netjsongraph/base/snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _

from .base import TimeStampedEditableModel


class AbstractSnapshot(TimeStampedEditableModel):
"""
NetJSON NetworkGraph Snapshot implementation
"""
topology = models.ForeignKey('django_netjsongraph.topology')
data = models.TextField(blank=False)
date = models.DateField(auto_now=True)

class Meta:
verbose_name_plural = _('snapshots')
abstract = True

def __str__(self):
return "{0}: {1}".format(self.topology.label, self.date)
35 changes: 34 additions & 1 deletion django_netjsongraph/base/topology.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
from collections import OrderedDict
from datetime import timedelta
from datetime import datetime, timedelta

from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
Expand Down Expand Up @@ -104,6 +104,10 @@ def link_model(self):
def node_model(self):
return self.node_set.model

@property
def snapshot_model(self):
return self.snapshot_set.model

def get_topology_data(self, data=None):
"""
gets latest topology data
Expand Down Expand Up @@ -248,6 +252,21 @@ def update(self, data=None):
link.full_clean()
link.save()

def save_snapshot(self, **kwargs):
"""
Saves the snapshot of topology
"""
Snapshot = self.snapshot_model
date = datetime.now().date()
options = dict(topology=self, date=date)
options.update(kwargs)
try:
s = Snapshot.objects.get(**options)
except:
s = Snapshot(**options)
s.data = self.json()
s.save()

def link_status_changed(self, link, status):
"""
determines if link status has changed,
Expand Down Expand Up @@ -304,3 +323,17 @@ def update_all(cls, label=None):
with log_failure('update', topology):
topology.update()
cls().link_model.delete_expired_links()

@classmethod
def save_snapshot_all(cls, label=None):
"""
- save snapshots of topoogies
- logs failures
"""
queryset = cls.objects.filter(published=True)
if label:
queryset = queryset.filter(label__icontains=label)
for topology in queryset:
print_info('Saving topology {0}'.format(topology))
with log_failure('save_snapshot', topology):
topology.save_snapshot()
13 changes: 13 additions & 0 deletions django_netjsongraph/management/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ def add_arguments(self, parser):

def handle(self, *args, **options):
self.topology_model.update_all(options['label'])


class BaseSaveSnapshotCommand(BaseCommand):
help = 'Save network topology graph snapshot'

def add_arguments(self, parser):
parser.add_argument('--label',
action='store',
default=None,
help='Will save snapshots of topologies containing label')

def handle(self, *args, **options):
self.topology_model.save_snapshot_all(options['label'])
6 changes: 6 additions & 0 deletions django_netjsongraph/management/commands/save_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import BaseSaveSnapshotCommand
from ...models import Topology


class Command(BaseSaveSnapshotCommand):
topology_model = Topology
34 changes: 34 additions & 0 deletions django_netjsongraph/migrations/0005_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-07-24 20:26
from __future__ import unicode_literals

import uuid

import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_netjsongraph', '0004_increased_addresses_maxlength'),
]

operations = [
migrations.CreateModel(
name='Snapshot',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('data', models.TextField()),
('date', models.DateField(auto_now=True)),
('topology', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_netjsongraph.Topology')),
],
options={
'abstract': False,
},
),
]
12 changes: 10 additions & 2 deletions django_netjsongraph/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .base.link import AbstractLink
from .base.node import AbstractNode
from .base.snapshot import AbstractSnapshot
from .base.topology import AbstractTopology


Expand All @@ -9,8 +10,15 @@ class Meta(AbstractTopology.Meta):


class Node(AbstractNode):
pass
class Meta(AbstractNode.Meta):
abstract = False


class Link(AbstractLink):
pass
class Meta(AbstractLink.Meta):
abstract = False


class Snapshot(AbstractSnapshot):
class Meta(AbstractSnapshot.Meta):
abstract = False
2 changes: 1 addition & 1 deletion django_netjsongraph/static/netjsongraph/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ input[type=text].readonly{

.njg-overlay{
top: auto;
bottom: 10px;
bottom: 50px;
}

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions django_netjsongraph/static/netjsongraph/css/style.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.njg-overlay,
.switcher,
.njg-metadata{
background: #fbfbfb;
border-radius: 3px;
Expand All @@ -24,6 +25,7 @@
min-width: 200px;
padding-bottom: 10px
}

.njg-overlay{
padding-top: 10px;
padding-bottom: 10px
Expand All @@ -40,6 +42,15 @@
margin: 8px 0;
}

.switcher{
top: auto;
bottom: 10px;
}

.switcher #dp{
margin: 5px;
}

.njg-node{
stroke-width: 7px;
stroke-alignment: inner;
Expand Down Expand Up @@ -130,3 +141,9 @@ body.frontend {
}
#legend .up{ border-color: #3acc38 }
#legend .down{ border-color: red }

.switcher{
position: absolute;
bottom: 5px;
right: 5px;
}

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions django_netjsongraph/static/netjsongraph/js/lib/jquery.min.js

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions django_netjsongraph/static/netjsongraph/js/topology-switcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
$(function() {
$('#dp').datepicker();
d = new Date();
month = d.getMonth() + 1;
day = d.getDate();
default_date = (month<10? '0':'') + month + '/' +
(day<10? '0':'') + day + '/' +
d.getFullYear();
$('#dp').val(default_date);
current_date = d.getFullYear() + '-' +
(month<10? '0':'') + month + '-' +
(day<10? '0':'') + day;
$('#submit').click(function() {
query_date = $('#dp').val();
date = query_date.split('/').reverse();
if(date != []){
var x = date[1];
date[1] = date[2];
date[2] = x;
}
date = date.join('-');
graph_url = $('.switcher').attr('graph-url') + '?date=' + date;
if(current_date == date){
graph_url = window.location.href;
}
body = $('body');
$.get(graph_url, function(html) {
body.html(html);
$('#dp').val(query_date);
});
});
});
Loading