diff --git a/flowspec/admin.py b/flowspec/admin.py index ddb49aadc9bed29f0a307518b193371f9519e81a..27d77c1c57a9b6623a5a51ed1ece47ba6d8afef0 100644 --- a/flowspec/admin.py +++ b/flowspec/admin.py @@ -31,6 +31,7 @@ from longerusername.forms import UserCreationForm, UserChangeForm class RuleAdmin(admin.ModelAdmin): form = RuleForm + list_display = ('name', 'status', 'applier_username', 'applier_peers', 'get_match', 'get_then', 'response', "comments") class RouteAdmin(admin.ModelAdmin): form = RouteForm diff --git a/flowspec/forms.py b/flowspec/forms.py index aaad8a0619f62e1ee9452503b8a150b6cfbd93d1..18f3a735f9104e5a0e760159e7f015f263678694 100644 --- a/flowspec/forms.py +++ b/flowspec/forms.py @@ -130,7 +130,7 @@ class RuleForm(forms.ModelForm): port = self.cleaned_data.get('port', None) destination = self.cleaned_data.get('destination', None) destinationports = self.cleaned_data.get('destinationport', None) - #user = self.cleaned_data.get('applier', None) + user = self.cleaned_data.get('applier', None) for route in existing_routes: if name != route.name: @@ -143,6 +143,7 @@ class RouteForm(forms.ModelForm): sourceport = PortRangeForm() destinationport = PortRangeForm() port = PortRangeForm() + rule = RuleForm() class Meta: model = Route @@ -179,11 +180,13 @@ class RouteForm(forms.ModelForm): raise forms.ValidationError(error) ## check if same rule exists with other name - #user = self.cleaned_data['applier'] - #if user.is_superuser: - # peers = Peer.objects.all() - #else: - # peers = user.userprofile.peers.all() + rule = self.cleaned_data['rule'] + user = rule.applier + print(vars(rule)) + if user.is_superuser: + peers = Peer.objects.all() + else: + peers = user.userprofile.peers.all() existing_routes = Route.objects.all() #existing_routes = existing_routes.filter(rule__applier__userprofile__peers__in=peers) name = self.cleaned_data.get('name', None) diff --git a/flowspec/models.py b/flowspec/models.py index 391d0d982bb41d7c00f1ac3ad90b038e00ee7a24..c576485c48aeb050b5181e3f607c9dd5ae2bd59c 100644 --- a/flowspec/models.py +++ b/flowspec/models.py @@ -300,6 +300,37 @@ class Rule(models.Model): } logger.info(mail_body, extra=d) + def get_absolute_url(self): + return reverse('rule-details', kwargs={'rule_slug': self.name}) + + def get_then(self): + ret = '' + then_statements = self.then.all() + for statement in then_statements: + if statement.action_value: + ret = "%s %s %s" %(ret, statement.action, statement.action_value) + else: + ret = "%s %s" %(ret, statement.action) + return ret + + get_then.short_description = 'Then statement' + get_then.allow_tags = True + + def get_match(self): + res = '' + if self.routes: + for r in self.routes.all(): + res = res + r.get_match() + return res + get_match.short_description = 'Match statement' + get_match.allow_tags = True + + def get_responses(self): + if self.routes: + return ', '.join([str(r.response) for r in self.routes.all()]) + else: + return '' + class Route(models.Model): name = models.SlugField(max_length=128, verbose_name=_("Name")) @@ -533,12 +564,13 @@ class Route(models.Model): def get_then(self): ret = '' - then_statements = self.then.all() - for statement in then_statements: - if statement.action_value: - ret = "%s %s %s" %(ret, statement.action, statement.action_value) - else: - ret = "%s %s" %(ret, statement.action) + if self.rule: + then_statements = self.rule.then.all() + for statement in then_statements: + if statement.action_value: + ret = "%s %s %s" %(ret, statement.action, statement.action_value) + else: + ret = "%s %s" %(ret, statement.action) return ret get_then.short_description = 'Then statement' diff --git a/flowspec/validators.py b/flowspec/validators.py index a02a4db443f090d8a5b65c47c78b4c4a70e9bed6..1fee33f1b33b04dbaa835a5282173aafb774677b 100644 --- a/flowspec/validators.py +++ b/flowspec/validators.py @@ -165,8 +165,8 @@ def clean_route_form(data): return _('Once destination port is matched, destination has to be filled as well. Either deselect destination port or fill destination address') if not (source or sourceports or ports or destination or destinationports): return _('Fill at least a Rule Match Condition') - if not user.is_superuser and then[0].action not in settings.UI_USER_THEN_ACTIONS: - return _('This action "%s" is not permitted') % (then[0].action) + #if not user or (not user.is_superuser and then[0].action not in settings.UI_USER_THEN_ACTIONS): + # return _('This action "%s" is not permitted') % (then[0].action) if then and then[0] else _("No Then action given.") def check_if_rule_exists(fields, queryset): diff --git a/flowspec/views.py b/flowspec/views.py index 78df4d6be783403aa94a494a4d0f18c1198ea1a0..6b228a933309fa2099ec43bcf011b66b6c20f2aa 100644 --- a/flowspec/views.py +++ b/flowspec/views.py @@ -89,7 +89,7 @@ def welcome(request): @login_required @never_cache def dashboard(request): - all_group_routes = [] + all_group_rules = [] message = '' try: peers = request.user.get_profile().peers.select_related('user_profile') @@ -104,13 +104,13 @@ def dashboard(request): ) if peers: if request.user.is_superuser: - all_group_routes = Route.objects.all().order_by('-last_updated')[:10] + all_group_rules = Rule.objects.all().order_by('-last_updated')[:10] else: query = Q() for peer in peers: query |= Q(applier__userprofile__in=peer.user_profile.all()) - all_group_routes = Route.objects.filter(query) - if all_group_routes is None: + all_group_rules = Rule.objects.filter(query) + if all_group_rules is None: message = 'You have not added any rules yet' else: message = 'You are not associated with a peer.' @@ -125,7 +125,7 @@ def dashboard(request): request, 'dashboard.html', { - 'routes': all_group_routes.prefetch_related( + 'routes': all_group_rules.prefetch_related( 'applier', 'applier', 'fragmenttype', @@ -161,7 +161,7 @@ def group_routes(request): @login_required @never_cache def group_routes_ajax(request): - all_group_routes = [] + all_group_rules = [] try: peers = request.user.get_profile().peers.prefetch_related('networks') except UserProfile.DoesNotExist: @@ -172,22 +172,22 @@ def group_routes_ajax(request): {'error': error} ) if request.user.is_superuser: - all_group_routes = Route.objects.all() + all_group_rules = Rule.objects.all() else: query = Q() for peer in peers: query |= Q(applier__userprofile__in=peer.user_profile.all()) - all_group_routes = Route.objects.filter(query) + all_group_rules = Route.objects.filter(query) jresp = {} - routes = build_routes_json(all_group_routes) - jresp['aaData'] = routes + rules = build_routes_json(all_group_rules) + jresp['aaData'] = rules return HttpResponse(json.dumps(jresp), mimetype='application/json') @login_required @never_cache def overview_routes_ajax(request): - all_group_routes = [] + all_group_rules = [] try: peers = request.user.get_profile().peers.all().select_related() except UserProfile.DoesNotExist: @@ -196,36 +196,41 @@ def overview_routes_ajax(request): query = Q() for peer in peers: query |= Q(applier__userprofile__in=peer.user_profile.all()) - all_group_routes = Route.objects.filter(query) + all_group_rules = Route.objects.filter(query) if request.user.is_superuser or request.user.has_perm('accounts.overview'): - all_group_routes = Route.objects.all() + all_group_rules = Route.objects.all() jresp = {} - routes = build_routes_json(all_group_routes) - jresp['aaData'] = routes + rules = build_routes_json(all_group_rules) + jresp['aaData'] = rules return HttpResponse(json.dumps(jresp), mimetype='application/json') -def build_routes_json(groutes): +def build_routes_json(grules): routes = [] - for r in groutes.prefetch_related( + for r in grules.prefetch_related( 'applier', - 'fragmenttype', - 'protocol', - 'dscp', + 'routes', ): rd = {} rd['id'] = r.pk - rd['port'] = r.port - rd['sourceport'] = r.sourceport - rd['destinationport'] = r.destinationport - # name with link to rule details rd['name'] = r.name rd['details'] = '<a href="%s">%s</a>' % (r.get_absolute_url(), r.name) + rd['routes'] = list() + for routei in r.routes.all(): + route = {} + route['port'] = routei.port + route['sourceport'] = routei.sourceport + route['destinationport'] = routei.destinationport + route['response'] = "%s" % routei.response + route['match'] = routei.get_match() + rd['routes'].append(route) + # name with link to rule details if not r.comments: rd['comments'] = 'Not Any' else: rd['comments'] = r.comments rd['match'] = r.get_match() + rd['response'] = r.get_responses() rd['then'] = r.get_then() rd['status'] = r.status # in case there is no applier (this should not occur) @@ -235,23 +240,8 @@ def build_routes_json(groutes): rd['applier'] = 'unknown' rd['peer'] = '' else: - peers = r.applier.get_profile().peers.select_related('networks') - username = None - for peer in peers: - if username: - break - for network in peer.networks.all(): - net = IPNetwork(network) - if IPNetwork(r.destination) in net: - username = peer.peer_name - break - try: - rd['peer'] = username - except UserProfile.DoesNotExist: - rd['peer'] = '' - + rd['peer'] = r.helper_get_matching_peers()[1] rd['expires'] = "%s" % r.expires - rd['response'] = "%s" % r.response routes.append(rd) return routes @@ -277,7 +267,12 @@ def add_route(request): return HttpResponseRedirect(reverse("group-routes")) if request.method == "GET": form = RouteForm(initial={'applier': applier}) - if not request.user.is_superuser: + form.fields['expires'] = forms.DateField() + form.fields['applier'] = forms.ModelChoiceField(queryset=User.objects.filter(pk=request.user.pk), required=True, empty_label=None) + if request.user.is_superuser: + form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.all().order_by('action'), required=True) + form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.all().order_by('protocol'), required=False) + else: form.fields['then'] = forms.ModelMultipleChoiceField(queryset=ThenAction.objects.filter(action__in=settings.UI_USER_THEN_ACTIONS).order_by('action'), required=True) form.fields['protocol'] = forms.ModelMultipleChoiceField(queryset=MatchProtocol.objects.filter(protocol__in=settings.UI_USER_PROTOCOLS).order_by('protocol'), required=False) return render_to_response('apply.html', {'form': form, @@ -828,12 +823,26 @@ def lookupShibAttr(attrmap, requestMeta): return '' +# show the details of specific route +@login_required +@never_cache +def ruledetails(request, rule_slug): + rule = get_object_or_404(Rule, name=rule_slug) + now = datetime.datetime.now() + last_msrm_delay_time = get_last_msrm_delay_time() + return render(request, 'flowspy/rule_details.html', { + 'rule': rule, + 'mytime': now, + 'last_msrm_delay_time': last_msrm_delay_time, + 'tz' : settings.TIME_ZONE, + 'route_comments_len' : len(str(rule.comments)) + }) + # show the details of specific route @login_required @never_cache def routedetails(request, route_slug): route = get_object_or_404(Route, name=route_slug) - #return render(request, 'flowspy/route_details.html', {'route': route}) now = datetime.datetime.now() last_msrm_delay_time = get_last_msrm_delay_time() return render(request, 'flowspy/route_details.html', { diff --git a/flowspy/urls.py b/flowspy/urls.py index 445eeb19f231e64bf075a7525a03d8ef85c6dbee..8f85f47e429000bb31d2e9fe13cf71c86ce96711 100644 --- a/flowspy/urls.py +++ b/flowspy/urls.py @@ -63,7 +63,8 @@ urlpatterns = patterns( ), url(r'^overview/?$', 'flowspec.views.overview', name="overview"), url(r'^api/', include(router.urls)), - url(r'^details/(?P<route_slug>[\w\-]+)/$', 'flowspec.views.routedetails', name="route-details"), + url(r'^details/(?P<rule_slug>[\w\-]+)/$', 'flowspec.views.ruledetails', name="rule-details"), + url(r'^routedetails/(?P<route_slug>[\w\-]+)/$', 'flowspec.views.routedetails', name="route-details"), url(r'^routestats/(?P<route_slug>[\w\-]+)/$', 'flowspec.views.routestats', name="routestats"), url(r'^admin/', include(admin.site.urls)), ) diff --git a/templates/dashboard.html b/templates/dashboard.html index c632ef3a8da4956eaecf689a9af5af9fbe9fbc05..9d02572fc3682dd7b4761a80f799c28cb579826b 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -37,24 +37,24 @@ <div class="panel-body"> <div class=panel>{{message}}</div> <ul class="timeline"> - {% for route in routes %} + {% for rule in rules %} <li class="{% cycle '' 'timeline-inverted' %}"> - {% if route.status == 'EXPIRED' or route.status == 'ADMININACTIVE' or route.status == 'INACTIVE' or route.status == 'OUTOFSYNC'%} - {% if route.status == 'EXPIRED' or route.status == 'ADMININACTIVE' or route.status == 'INACTIVE' %} + {% if rule.status == 'EXPIRED' or rule.status == 'ADMININACTIVE' or rule.status == 'INACTIVE' or rule.status == 'OUTOFSYNC'%} + {% if rule.status == 'EXPIRED' or rule.status == 'ADMININACTIVE' or rule.status == 'INACTIVE' %} <div class="timeline-badge"><i class="fa fa-adjust"></i></div> {% else %} - {% if route.status == 'OUTOFSYNC' %} + {% if rule.status == 'OUTOFSYNC' %} <div class="timeline-badge danger"><i class="fa fa-bug"></i></div> {% else %} <div class="timeline-badge danger"><i class="fa fa-exclamation"></i></div> {% endif %} {% endif %} {% else %} - {% if route.status == 'ACTIVE' %} + {% if rule.status == 'ACTIVE' %} <div class="timeline-badge success"><i class="fa fa-shield"></i></div> {% else %} - {% if route.status == 'PENDING' %} + {% if rule.status == 'PENDING' %} <div class="timeline-badge info"><i class="fa fa-spinner"></i></div> {% else %} <div class="timeline-badge warning"><i class="fa fa-exclamation"></i></div> @@ -64,60 +64,60 @@ <div class="timeline-panel"> <div class="timeline-heading"> - <h4 class="timeline-title"><a href="{{ route.get_absolute_url }}">{{route.name}}</a></h4> + <h4 class="timeline-title"><a href="{{ rule.get_absolute_url }}">{{rule.name}}</a></h4> <p> - <small class="text-muted"><i class="fa fa-pencil-square-o"></i> {% trans "Last update" %}: {{route.last_updated}} {% trans "by" %} {{route.applier}}</small> + <small class="text-muted"><i class="fa fa-pencil-square-o"></i> {% trans "Last update" %}: {{rule.last_updated}} {% trans "by" %} {{rule.applier}}</small> </p> <p> - <small class="text-muted"><i class="fa fa-clock-o"></i> {% trans "Expires" %}: {{ route.expires|date:"d M y" }}</small> + <small class="text-muted"><i class="fa fa-clock-o"></i> {% trans "Expires" %}: {{ rule.expires|date:"d M y" }}</small> </p> </div> <div class="timeline-body"> <small> <p> - {% if route.status == 'EXPIRED' or route.status == 'ADMININACTIVE' or route.status == 'INACTIVE' or route.status == 'OUTOFSYNC'%} - {% if route.status == 'EXPIRED' or route.status == 'ADMININACTIVE' or route.status == 'INACTIVE' %} + {% if rule.status == 'EXPIRED' or rule.status == 'ADMININACTIVE' or rule.status == 'INACTIVE' or rule.status == 'OUTOFSYNC'%} + {% if rule.status == 'EXPIRED' or rule.status == 'ADMININACTIVE' or rule.status == 'INACTIVE' %} <span class="label label-default">DEACTIVATED</span> {% else %} - {% if route.status == 'OUTOFSYNC' %} + {% if rule.status == 'OUTOFSYNC' %} <span class="label label-danger">ERROR</span> {% else %} - <span class="label label-info">{{route.status}}</span> + <span class="label label-info">{{rule.status}}</span> {% endif %} {% endif %} {% else %} - {% if route.status == 'ACTIVE' %} - <span class="label label-success">{{route.status}}</span> + {% if rule.status == 'ACTIVE' %} + <span class="label label-success">{{rule.status}}</span> {% else %} - {% if route.status == 'PENDING' %} - <span class="label label-info">{{route.status}}</span> + {% if rule.status == 'PENDING' %} + <span class="label label-info">{{rule.status}}</span> {% else %} - <span class="label label-warning">{{route.status}}</span> + <span class="label label-warning">{{rule.status}}</span> {% endif %} {% endif %} {% endif %} </p> - {{ route.get_match|safe|escape }} + {{ rule.get_match|safe|escape }} <dl class="dl-horizontal"> - <dt>Then</dt><dd>{{ route.get_then }}</dd> + <dt>Then</dt><dd>{{ rule.get_then }}</dd> </dl> </small> <div> - {% ifequal route.status 'ACTIVE' %} - <a href="{% url edit-route route.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{route.pk}}">{% trans "Edit" %}</a> - <button class="del_buttonpre btn-outline btn btn-xs btn-warning" id="{{route.name}}" data-routename="{{route.name}}">{% trans "Deactivate" %}</button> + {% ifequal rule.status 'ACTIVE' %} + <a href="{% url edit-route rule.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{rule.pk}}">{% trans "Edit" %}</a> + <button class="del_buttonpre btn-outline btn btn-xs btn-warning" id="{{rule.name}}" data-routename="{{rule.name}}">{% trans "Deactivate" %}</button> {% else %} - {% if route.status == 'EXPIRED' or route.status == 'ADMININACTIVE' or route.status == 'INACTIVE' %} - <a href="{% url edit-route route.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{route.pk}}" type="button">{% trans "Reactivate" %}</a> + {% if rule.status == 'EXPIRED' or rule.status == 'ADMININACTIVE' or rule.status == 'INACTIVE' %} + <a href="{% url edit-route rule.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{rule.pk}}" type="button">{% trans "Reactivate" %}</a> {% else %} - {% ifequal route.status 'OUTOFSYNC' %} - <a href="{% url edit-route route.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{route.pk}}">{% trans "ReSync" %}</a> + {% ifequal rule.status 'OUTOFSYNC' %} + <a href="{% url edit-route rule.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{rule.pk}}">{% trans "ReSync" %}</a> {% else %} - {% ifequal route.status 'ERROR' %} - <a href="{% url edit-route route.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{route.pk}}">{% trans "Fix it!" %}</a> + {% ifequal rule.status 'ERROR' %} + <a href="{% url edit-route rule.name %}" class="btn-info btn btn-xs btn-outline" id="edit_button_{{rule.pk}}">{% trans "Fix it!" %}</a> {% else %} - {% endifequal %} diff --git a/templates/flowspy/route_details.html b/templates/flowspy/route_details.html index 3558476b111190d2f8acf478c47d7dd35a9eec27..fa740c95ede95562e94a61e4be262a93cf3ba15a 100644 --- a/templates/flowspy/route_details.html +++ b/templates/flowspy/route_details.html @@ -19,17 +19,19 @@ function myreloadPage() { </div> <div class="row"> <div class="col-md-12"> - <div> - <i class="fa fa-clock-o"></i> {% trans "Expires" %}: {{ route.expires|date:"d M y" }} - </div> - </div> - <div class="col-md-12"> - <div> - <i class="fa fa-pencil-square-o"></i> {% trans "Last rule edit" %}: {{route.last_updated}} {% trans "by" %} {{route.applier}} - </div> <div> <h2>{% trans 'About' %}</h2> - {{ route.get_then }} + + {% if route.comments %} + <p> + <div> + Comments: {{ route.comments|slice:"0:300" }} + {% if route_comments_len > 300 %} + ... + {% endif %} + </div> + {% endif %} + {% trans 'all'%} {% if route.protocol.count %} {% for proto in route.protocol.all %} @@ -53,49 +55,21 @@ function myreloadPage() { {% endfor %} ) {% endif %} - {% if route.status = 'EXPIRED' or route.status = 'ADMININACTIVE' or route.status = 'INACTIVE' %} - <span class="label label-default">DEACTIVATED</span> - {% elif route.status = 'OUTOFSYNC' %} - <span class="label label-danger">ERROR</span> - {% elif route.status = 'ACTIVE' %} - <span class="label label-success">{{ route.status }}</span> - {% elif route.status = 'PENDING' %} - <span class="label label-info">{{ route.status }}</span> - {% else %} - <span class="label label-danger">{{ route.status }}</span> - {% endif %} - {% if route.status != 'PENDING' %} - - <a href="{% url edit-route route.name %}" class="btn-info btn btn-sm btn-outline">{% trans "Edit" %}</a> - {% if route.status = 'ACTIVE' %} - <button class="del_button btn-warning btn btn-sm btn-outline" id="{{ route.name }}" data-routename="{{ route.name }}">{% trans "Deactivate" %}</button> - {% endif %} - {% endif %} - - {% if route.comments %} - <p> - <div> - Comments: {{ route.comments|slice:"0:300" }} - {% if route_comments_len > 300 %} - ... - {% endif %} - </div> - {% endif %} - </div> - <div> - <h2>Statistics</h2> - <div>(all times are in {{ tz }}; current System time: {{ mytime|date:'Y-m-d H:i' }}, active rules will be updated every 5 minutes, last duration for measurement was {{ last_msrm_delay_time }})</div> - <div><span id="traffic-plot-loading">(Loading data...)</span> - <h3>Number of packets (absolute)</h3> - <div><canvas id="traffic-plot-pkts-abs" width=200 height=200></canvas></div> - <h3>Number of packets (relative)</h3> - <div><canvas id="traffic-plot-pkts-rel" width=200 height=200></canvas></div> - <h3>Number of bytes (absolute)</h3> - <div><canvas id="traffic-plot-bytes-abs" width=200 height=200></canvas></div> - <h3>Number of bytes (relative)</h3> - <div><canvas id="traffic-plot-bytes-rel" width=200 height=200></canvas></div> + <div> + <h2>Statistics</h2> + <div>(all times are in {{ tz }}; current System time: {{ mytime|date:'Y-m-d H:i' }}, active rules will be updated every 5 minutes, last duration for measurement was {{ last_msrm_delay_time }})</div> + <div><span id="traffic-plot-loading">(Loading data...)</span> + <h3>Number of packets (absolute)</h3> + <div><canvas id="traffic-plot-pkts-abs" width=200 height=200></canvas></div> + <h3>Number of packets (relative)</h3> + <div><canvas id="traffic-plot-pkts-rel" width=200 height=200></canvas></div> + <h3>Number of bytes (absolute)</h3> + <div><canvas id="traffic-plot-bytes-abs" width=200 height=200></canvas></div> + <h3>Number of bytes (relative)</h3> + <div><canvas id="traffic-plot-bytes-rel" width=200 height=200></canvas></div> + </div> + <div><a href="{% url routestats route.name %}">Download raw data in JSON</a></div> </div> - <div><a href="{% url routestats route.name %}">Download raw data in JSON</a></div> </div> {% comment %}check if graphs plugin in installed apps{% endcomment %} {% if 'graphs' in INSTALLED_APPS %} @@ -115,9 +89,9 @@ function myreloadPage() { </div> {% endif %} - {% if route.comments %} + {% if rule.comments %} <p> - <div><h2>Comments</h2><pre>{{ route.comments }}</pre></div> + <div><h2>Comments</h2><pre>{{ rule.comments }}</pre></div> {% endif %} </div> @@ -291,15 +265,18 @@ function plotGraph(data) } $(document).ready(function() { - var statsurl = "{% url routestats route.name %}"; - $.get(statsurl).done(function(data) { - if (data["error"]) { - $("#traffic-plot-loading").text("No data, try later"); - } else { - $("#traffic-plot-loading").hide(); - plotGraph(data); - } - }); + var statsurls = Array({% for route in rule.routes.all %}"{% url routestats route.name %}",{% endfor %}); + for (i = 0; i < statsurls.length; i++) { + statsurl = statsurls[i]; + $.get(statsurl).done(function(data) { + if (data["error"]) { + $("#traffic-plot-loading").text("No data, try later"); + } else { + $("#traffic-plot-loading").hide(); + plotGraph(data); + } + }); + } }); </script> diff --git a/templates/flowspy/rule_details.html b/templates/flowspy/rule_details.html new file mode 100644 index 0000000000000000000000000000000000000000..5120df8812b290bd80f65f7226b7692735346ec1 --- /dev/null +++ b/templates/flowspy/rule_details.html @@ -0,0 +1,387 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block contentplaceholder %} +<script> +setInterval("myreloadPage()", 30*1000); +function myreloadPage() { + //location.reload(true); + location.reload(false); +} +</script> + +<div class="row"> + <div class="col-lg-12"> + <h1 class="page-header">Rule {{ rule.name }}</h1> + <div>(all times are in {{ tz }}; current System time: {{ mytime|date:'Y-m-d H:i' }})</div> + <br> + </div> +</div> +<div class="row"> + <div class="col-md-12"> + <div> + <i class="fa fa-clock-o"></i> {% trans "Expires" %}: {{ rule.expires|date:"d M y" }} + </div> + </div> + <div class="col-md-12"> + <div> + <i class="fa fa-pencil-square-o"></i> {% trans "Last rule edit" %}: {{rule.last_updated}} {% trans "by" %} {{rule.applier}} + </div> + <div> + <h2>{% trans 'About' %}</h2> + {{ rule.get_then }} + + {% if rule.status = 'EXPIRED' or rule.status = 'ADMININACTIVE' or rule.status = 'INACTIVE' %} + <span class="label label-default">DEACTIVATED</span> + {% elif rule.status = 'OUTOFSYNC' %} + <span class="label label-danger">ERROR</span> + {% elif rule.status = 'ACTIVE' %} + <span class="label label-success">{{ rule.status }}</span> + {% elif rule.status = 'PENDING' %} + <span class="label label-info">{{ rule.status }}</span> + {% else %} + <span class="label label-danger">{{ rule.status }}</span> + {% endif %} + {% if rule.status != 'PENDING' %} + + {% if rule.routes.count == 1 %} + <a href="{% url edit-route rule.name %}" class="btn-info btn btn-sm btn-outline">{% trans "Edit" %}</a> + {% endif %} + {% if rule.status = 'ACTIVE' %} + <button class="del_button btn-warning btn btn-sm btn-outline" id="{{ rule.name }}" data-routename="{{ rule.name }}">{% trans "Deactivate" %}</button> + {% endif %} + {% endif %} + + {% if rule.comments %} + <p> + <div> + Comments: {{ rule.comments|slice:"0:300" }} + {% if rule_comments_len > 300 %} + ... + {% endif %} + </div> + {% endif %} + + <h3>Routes:</h3> + + {% for route in rule.routes.all %} + {% trans 'all'%} + {% if route.protocol.count %} + {% for proto in route.protocol.all %} + {{ proto }} {% if not forloop.last %},{% endif %} + {% endfor %} + {% endif %} + {% trans 'traffic from' %} + {{ route.source }} + {% if route.sourceport %} {% trans 'port' %} + {{ route.sourceport }} + {% endif %} + {% trans 'to' %} + {{ route.destination }} + {% if route.destinationport %} {% trans 'port' %} + {{ route.destinationport }} + {% endif %} + {% if route.fragmenttype.count %} + ({% trans 'Fragmentypes' %}: + {% for f in route.fragmenttype.all %} + {{ f }} {% if not forloop.last %},{% endif %} + {% endfor %} + ) + {% endif %} + {% if rule.routes.count == 1 %} + <div> + <h2>Statistics</h2> + <div>(all times are in {{ tz }}; current System time: {{ mytime|date:'Y-m-d H:i' }}, active rules will be updated every 5 minutes, last duration for measurement was {{ last_msrm_delay_time }})</div> + <div><span id="traffic-plot-loading">(Loading data...)</span> + <h3>Number of packets (absolute)</h3> + <div><canvas id="traffic-plot-pkts-abs" width=200 height=200></canvas></div> + <h3>Number of packets (relative)</h3> + <div><canvas id="traffic-plot-pkts-rel" width=200 height=200></canvas></div> + <h3>Number of bytes (absolute)</h3> + <div><canvas id="traffic-plot-bytes-abs" width=200 height=200></canvas></div> + <h3>Number of bytes (relative)</h3> + <div><canvas id="traffic-plot-bytes-rel" width=200 height=200></canvas></div> + </div> + <div><a href="{% url routestats route.name %}">Download raw data in JSON</a></div> + {% else %} + <div><a href="{% url route-details route.name %}">Details</a></div> + {% endif %} + {% endfor %} + </div> + </div> + {% comment %}check if graphs plugin in installed apps{% endcomment %} + {% if 'graphs' in INSTALLED_APPS %} + <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" /> + + <div class="col-md-12 graphs-wrapper" style="display: none"> + <h3>{% trans 'Graphs' %}</h3> + <div class="col-md-3"> + <div id="reportrange" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc;"> + <i class="glyphicon glyphicon-calendar fa fa-calendar"></i> + <span></span> <b class="caret"></b> + </div> + </div> + <div class="graphs col-md-12" data-url="{% url graphs route.name %}" > + loading... + </div> + </div> + {% endif %} + + {% if rule.comments %} + <p> + <div><h2>Comments</h2><pre>{{ rule.comments }}</pre></div> + {% endif %} + +</div> +{% endblock %} + +{% block pagejsbottom %} + +<script src="{{STATIC_URL}}js/Chart.min.js" type="text/javascript"></script> + + +<script src="{{STATIC_URL}}js/moment.min.js"></script> +<!--<script src="{{STATIC_URL}}js/hammer.min.js"></script>--> +<script src="{{STATIC_URL}}js/chartjs-plugin-zoom.min.js"></script> + +<script type="text/javascript"> +function plotGraph(data) +{ + var xdata = Array(); + var ydata = Array(); + var ydatarel = Array(); + var ybytesdata = Array(); + var ybytesdatarel = Array(); + + for (i=0; i<data["data"].length; i++) { + var d = data["data"][data["data"].length - 1 - i]; + xdata[i] = d.ts.replace(/\..*/, '').replace('T', ' '); + ydata[i] = d.value.packets; + ybytesdata[i] = d.value.bytes; + if (i == 0) { + ydatarel[i] = 0; + ybytesdatarel[i] = 0; + } else { + delta = (ydata[i]===undefined) ? undefined : (ydata[i-1]===undefined) ? ydata[i] : (ydata[i] - ydata[i-1]); + ydatarel[i] = (delta===undefined || delta>=0) ? delta : 0; + + bytesdelta = (ybytesdata[i]===undefined) ? undefined : (ybytesdata[i-1]===undefined) ? ybytesdata[i] : (ybytesdata[i] - ybytesdata[i-1]); + ybytesdatarel[i] = (bytesdelta===undefined || bytesdelta>=0) ? bytesdelta : 0; + } + } + + var graphpktsabs = document.getElementById("traffic-plot-pkts-abs"); + var graphpktsrel = document.getElementById("traffic-plot-pkts-rel"); + var graphbytesabs = document.getElementById("traffic-plot-bytes-abs"); + var graphbytesrel = document.getElementById("traffic-plot-bytes-rel"); + graphpktsabs.width = 80; + graphpktsabs.height = 20; + graphpktsrel.width = 80; + graphpktsrel.height = 20; + graphbytesabs.width = 80; + graphbytesabs.height = 20; + graphbytesrel.width = 80; + graphbytesrel.height = 20; + + var graphabssetting = { + type: 'line', + data: { + labels: xdata, + datasets: [{ + label: '# packets', + data: ydata, + borderWidth: 2, + borderColor: "#3c37c6", + pointBackgroundColor: "#3c37c6", + backgroundColor: "#99bfff" + }] + }, + options: { + elements: { + line: { tension: 0, } // disables bezier curves + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + }, zoom: { + enabled: true, + drag: true, + mode: 'x', + //limits: { + // max: 10, + // min: 0.5 + //} + } + + } + } + var graphrelsetting = { + type: 'bar', + data: { + labels: xdata, + datasets: [{ + label: '# packets', + data: ydatarel, + borderWidth: 2, + borderColor: "#c63737", + pointBackgroundColor: "#c63737", + backgroundColor: "#ff877a" + }] + }, + options: { + elements: { + line: { tension: 0, } // disables bezier curves + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + } + } + } + var graphbytesabssetting = { + type: 'line', + data: { + labels: xdata, + datasets: [{ + label: '# bytes', + data: ybytesdata, + borderWidth: 2, + borderColor: "#3c37c6", + pointBackgroundColor: "#3c37c6", + backgroundColor: "#99bfff" + }] + }, + options: { + elements: { + line: { tension: 0, } // disables bezier curves + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + } + } + } + var graphbytesrelsetting = { + type: 'bar', + data: { + labels: xdata, + datasets: [{ + label: '# bytes', + data: ybytesdatarel, + borderWidth: 2, + borderColor: "#c63737", + pointBackgroundColor: "#c63737", + backgroundColor: "#ff877a" + }] + }, + options: { + elements: { + line: { tension: 0, } // disables bezier curves + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + } + } + } + var pktsabsChart = new Chart(graphpktsabs, graphabssetting); + var pktsrelChart = new Chart(graphpktsrel, graphrelsetting); + var bytesabsChart = new Chart(graphbytesabs, graphbytesabssetting); + var bytesrelChart = new Chart(graphbytesrel, graphbytesrelsetting); +} + +$(document).ready(function() { + var statsurls = Array({% for route in rule.routes.all %}"{% url routestats route.name %}",{% endfor %}); + for (i = 0; i < statsurls.length; i++) { + statsurl = statsurls[i]; + $.get(statsurl).done(function(data) { + if (data["error"]) { + $("#traffic-plot-loading").text("No data, try later"); + } else { + $("#traffic-plot-loading").hide(); + plotGraph(data); + } + }); + } +}); +</script> + + +{% if 'graphs' in INSTALLED_APPS %} +<script src="https://cdn.jsdelivr.net/momentjs/2.9.0/moment.min.js"></script> +<script src="https://cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script> +<script type="text/javascript"> + $(document).ready(function () { + + var url = $('.graphs').data('url'); + var start = moment().subtract(1, 'days').format('X') + var end = moment().format('X') + + function cb(start, end) { + $('#reportrange span').html(start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY')); + $('.graphs').load(url + '?start=' + start.format('X') + '&end=' + end.format('X'), function () { + $('.graphs-wrapper').show(); + }); + } + cb(moment().subtract(29, 'days'), moment()); + + $('#reportrange').daterangepicker({ + ranges: { + 'Today': [moment().subtract(1, 'days'), moment()], + 'Yesterday': [moment().subtract(2, 'days'), moment().subtract(1, 'days')], + 'Last 7 Days': [moment().subtract(6, 'days'), moment()], + 'Last 30 Days': [moment().subtract(29, 'days'), moment()], + 'This Month': [moment().startOf('month'), moment().endOf('month')], + 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] + } + }, cb(moment().subtract(1, 'days'), moment())); + + $('body').on('apply.daterangepicker', '#reportrange', function(ev, picker) { + cb(picker.startDate, picker.endDate); + }); + + + }); +</script> +{% endif %} + +<script type="text/javascript"> + + var delete_triggerd = false; + + $(document).ready(function () { + $('body').on('click', ".del_button", function(){ + if (delete_triggerd) + return; + delete_triggerd = true; + + last_element = false; + var my = $(this); + my.html('Deactivating...') + var routename = $(this).data("routename"); + var delurl = "{% url delete-route 'route_placeholder'%}".replace('route_placeholder', routename.toString()); + $.ajax({ + type: 'POST', + url: delurl, + cache: false, + success: function(data) { + $('.del_button').addClass('disabled').text('Done'); + } + }); + return false; + }); + }); +</script> +{% endblock %}