8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-27 17:33:45 +00:00
re2o/logs/views.py

464 lines
17 KiB
Python

# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec
# Copyright © 2017 Augustin Lemesle
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# App de gestion des statistiques pour re2o
# Gabriel Détraz
# Gplv2
"""
Vues des logs et statistiques générales.
La vue index générale affiche une selection des dernières actions,
classées selon l'importance, avec date, et user formatés.
Stats_logs renvoie l'ensemble des logs.
Les autres vues sont thématiques, ensemble des statistiques et du
nombre d'objets par models, nombre d'actions par user, etc
"""
from __future__ import unicode_literals
from django.urls import reverse
from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Count, Max
from reversion.models import Revision
from reversion.models import Version, ContentType
from time import time
from users.models import (
User,
ServiceUser,
School,
ListRight,
ListShell,
Ban,
Whitelist,
Adherent,
Club
)
from cotisations.models import (
Facture,
Vente,
Article,
Banque,
Paiement,
Cotisation
)
from machines.models import (
Machine,
MachineType,
IpType,
Extension,
Interface,
Domain,
IpList,
OuverturePortList,
Service,
Vlan,
Nas,
SOA,
Mx,
Ns
)
from topologie.models import (
Switch,
Port,
Room,
Stack,
ModelSwitch,
ConstructorSwitch,
AccessPoint
)
from preferences.models import GeneralOption
from re2o.views import form
from re2o.utils import (
all_whitelisted,
all_baned,
all_has_access,
all_adherent,
)
from re2o.acl import (
can_view_all,
can_view_app,
can_edit_history,
)
from re2o.utils import all_active_assigned_interfaces_count
from re2o.utils import all_active_interfaces_count, SortTable
STATS_DICT = {
0: ["Tout", 36],
1: ["1 mois", 1],
2: ["2 mois", 2],
3: ["6 mois", 6],
4: ["1 an", 12],
5: ["2 an", 24],
}
@login_required
@can_view_app('logs')
def index(request):
"""Affiche les logs affinés, date reformatées, selectionne
les event importants (ajout de droits, ajout de ban/whitelist)"""
pagination_number = GeneralOption.get_cached_value('pagination_number')
# The types of content kept for display
content_type_filter = ['ban', 'whitelist', 'vente', 'interface', 'user']
# Select only wanted versions
versions = Version.objects.filter(
content_type__in=ContentType.objects.filter(
model__in=content_type_filter
)
).select_related('revision')
versions = SortTable.sort(
versions,
request.GET.get('col'),
request.GET.get('order'),
SortTable.LOGS_INDEX
)
paginator = Paginator(versions, pagination_number)
page = request.GET.get('page')
try:
versions = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
versions = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
versions = paginator.page(paginator.num_pages)
# Force to have a list instead of QuerySet
versions.count(0)
# Items to remove later because invalid
to_remove = []
# Parse every item (max = pagination_number)
for i in range(len(versions.object_list)):
if versions.object_list[i].object:
version = versions.object_list[i]
versions.object_list[i] = {
'rev_id': version.revision.id,
'comment': version.revision.comment,
'datetime': version.revision.date_created.strftime(
'%d/%m/%y %H:%M:%S'
),
'username':
version.revision.user.get_username()
if version.revision.user else '?',
'user_id': version.revision.user_id,
'version': version}
else:
to_remove.insert(0, i)
# Remove all tagged invalid items
for i in to_remove:
versions.object_list.pop(i)
return render(request, 'logs/index.html', {'versions_list': versions})
@login_required
@can_view_all(GeneralOption)
def stats_logs(request):
"""Affiche l'ensemble des logs et des modifications sur les objets,
classés par date croissante, en vrac"""
pagination_number = GeneralOption.get_cached_value('pagination_number')
revisions = Revision.objects.all().select_related('user')\
.prefetch_related('version_set__object')
revisions = SortTable.sort(
revisions,
request.GET.get('col'),
request.GET.get('order'),
SortTable.LOGS_STATS_LOGS
)
paginator = Paginator(revisions, pagination_number)
page = request.GET.get('page')
try:
revisions = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
revisions = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
revisions = paginator.page(paginator.num_pages)
return render(request, 'logs/stats_logs.html', {
'revisions_list': revisions
})
@login_required
@can_edit_history
def revert_action(request, revision_id):
""" Annule l'action en question """
try:
revision = Revision.objects.get(id=revision_id)
except Revision.DoesNotExist:
messages.error(request, u"Revision inexistante")
if request.method == "POST":
revision.revert()
messages.success(request, "L'action a été supprimée")
return redirect(reverse('logs:index'))
return form({
'objet': revision,
'objet_name': revision.__class__.__name__
}, 'logs/delete.html', request)
@login_required
@can_view_all(IpList)
@can_view_all(Interface)
@can_view_all(User)
def stats_general(request):
"""Statistiques générales affinées sur les ip, activées, utilisées par
range, et les statistiques générales sur les users : users actifs,
cotisants, activés, archivés, etc"""
ip_dict = dict()
for ip_range in IpType.objects.select_related('vlan').all():
all_ip = IpList.objects.filter(ip_type=ip_range)
used_ip = Interface.objects.filter(ipv4__in=all_ip).count()
active_ip = all_active_assigned_interfaces_count().filter(
ipv4__in=IpList.objects.filter(ip_type=ip_range)
).count()
ip_dict[ip_range] = [ip_range, ip_range.vlan, all_ip.count(),
used_ip, active_ip, all_ip.count()-used_ip]
_all_adherent = all_adherent()
_all_has_access = all_has_access()
_all_baned = all_baned()
_all_whitelisted = all_whitelisted()
_all_active_interfaces_count = all_active_interfaces_count()
_all_active_assigned_interfaces_count = all_active_assigned_interfaces_count()
stats = [
[["Categorie", "Nombre d'utilisateurs (total club et adhérents)", "Nombre d'adhérents", "Nombre de clubs"], {
'active_users': [
"Users actifs",
User.objects.filter(state=User.STATE_ACTIVE).count(),
Adherent.objects.filter(state=Adherent.STATE_ACTIVE).count(),
Club.objects.filter(state=Club.STATE_ACTIVE).count()],
'inactive_users': [
"Users désactivés",
User.objects.filter(state=User.STATE_DISABLED).count(),
Adherent.objects.filter(state=Adherent.STATE_DISABLED).count(),
Club.objects.filter(state=Club.STATE_DISABLED).count()],
'archive_users': [
"Users archivés",
User.objects.filter(state=User.STATE_ARCHIVE).count(),
Adherent.objects.filter(state=Adherent.STATE_ARCHIVE).count(),
Club.objects.filter(state=Club.STATE_ARCHIVE).count()],
'adherent_users': [
"Cotisant à l'association",
_all_adherent.count(),
_all_adherent.exclude(adherent__isnull=True).count(),
_all_adherent.exclude(club__isnull=True).count()],
'connexion_users': [
"Utilisateurs bénéficiant d'une connexion",
_all_has_access.count(),
_all_has_access.exclude(adherent__isnull=True).count(),
_all_has_access.exclude(club__isnull=True).count()],
'ban_users': [
"Utilisateurs bannis",
_all_baned.count(),
_all_baned.exclude(adherent__isnull=True).count(),
_all_baned.exclude(club__isnull=True).count()],
'whitelisted_user': [
"Utilisateurs bénéficiant d'une connexion gracieuse",
_all_whitelisted.count(),
_all_whitelisted.exclude(adherent__isnull=True).count(),
_all_whitelisted.exclude(club__isnull=True).count()],
'actives_interfaces': [
"Interfaces actives (ayant accès au reseau)",
_all_active_interfaces_count.count(),
_all_active_interfaces_count.exclude(
machine__user__adherent__isnull=True
).count(),
_all_active_interfaces_count.exclude(
machine__user__club__isnull=True
).count()],
'actives_assigned_interfaces': [
"Interfaces actives et assignées ipv4",
_all_active_assigned_interfaces_count.count(),
_all_active_assigned_interfaces_count.exclude(
machine__user__adherent__isnull=True
).count(),
_all_active_assigned_interfaces_count.exclude(
machine__user__club__isnull=True
).count()]
}],
[["Range d'ip", "Vlan", "Nombre d'ip totales", "Ip assignées",
"Ip assignées à une machine active", "Ip non assignées"], ip_dict]
]
return render(request, 'logs/stats_general.html', {'stats_list': stats})
@login_required
@can_view_app('users')
@can_view_app('cotisations')
@can_view_app('machines')
@can_view_app('topologie')
def stats_models(request):
"""Statistiques générales, affiche les comptages par models:
nombre d'users, d'écoles, de droits, de bannissements,
de factures, de ventes, de banque, de machines, etc"""
stats = {
'Users': {
'users': [User.PRETTY_NAME, User.objects.count()],
'adherents': [Adherent.PRETTY_NAME, Adherent.objects.count()],
'clubs': [Club.PRETTY_NAME, Club.objects.count()],
'serviceuser': [ServiceUser.PRETTY_NAME,
ServiceUser.objects.count()],
'school': [School.PRETTY_NAME, School.objects.count()],
'listright': [ListRight.PRETTY_NAME, ListRight.objects.count()],
'listshell': [ListShell.PRETTY_NAME, ListShell.objects.count()],
'ban': [Ban.PRETTY_NAME, Ban.objects.count()],
'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
},
'Cotisations': {
'factures': [Facture.PRETTY_NAME, Facture.objects.count()],
'vente': [Vente.PRETTY_NAME, Vente.objects.count()],
'cotisation': [Cotisation.PRETTY_NAME, Cotisation.objects.count()],
'article': [Article.PRETTY_NAME, Article.objects.count()],
'banque': [Banque.PRETTY_NAME, Banque.objects.count()],
},
'Machines': {
'machine': [Machine.PRETTY_NAME, Machine.objects.count()],
'typemachine': [MachineType.PRETTY_NAME,
MachineType.objects.count()],
'typeip': [IpType.PRETTY_NAME, IpType.objects.count()],
'extension': [Extension.PRETTY_NAME, Extension.objects.count()],
'interface': [Interface.PRETTY_NAME, Interface.objects.count()],
'alias': [Domain.PRETTY_NAME,
Domain.objects.exclude(cname=None).count()],
'iplist': [IpList.PRETTY_NAME, IpList.objects.count()],
'service': [Service.PRETTY_NAME, Service.objects.count()],
'ouvertureportlist': [
OuverturePortList.PRETTY_NAME,
OuverturePortList.objects.count()
],
'vlan': [Vlan.PRETTY_NAME, Vlan.objects.count()],
'SOA': [SOA.PRETTY_NAME, SOA.objects.count()],
'Mx': [Mx.PRETTY_NAME, Mx.objects.count()],
'Ns': [Ns.PRETTY_NAME, Ns.objects.count()],
'nas': [Nas.PRETTY_NAME, Nas.objects.count()],
},
'Topologie': {
'switch': [Switch.PRETTY_NAME, Switch.objects.count()],
'bornes': [AccessPoint.PRETTY_NAME, AccessPoint.objects.count()],
'port': [Port.PRETTY_NAME, Port.objects.count()],
'chambre': [Room.PRETTY_NAME, Room.objects.count()],
'stack': [Stack.PRETTY_NAME, Stack.objects.count()],
'modelswitch': [
ModelSwitch.PRETTY_NAME,
ModelSwitch.objects.count()
],
'constructorswitch': [
ConstructorSwitch.PRETTY_NAME,
ConstructorSwitch.objects.count()
],
},
'Actions effectuées sur la base':
{
'revision': ["Nombre d'actions", Revision.objects.count()],
},
}
return render(request, 'logs/stats_models.html', {'stats_list': stats})
@login_required
@can_view_app('users')
def stats_users(request):
"""Affiche les statistiques base de données aggrégées par user :
nombre de machines par user, d'etablissements par user,
de moyens de paiements par user, de banque par user,
de bannissement par user, etc"""
onglet = request.GET.get('onglet')
try:
_search_field = STATS_DICT[onglet]
except KeyError:
_search_field = STATS_DICT[0]
onglet = 0
stats = {
'Utilisateur': {
'Machines': User.objects.annotate(
num=Count('machine')
).order_by('-num')[:10],
'Facture': User.objects.annotate(
num=Count('facture')
).order_by('-num')[:10],
'Bannissement': User.objects.annotate(
num=Count('ban')
).order_by('-num')[:10],
'Accès gracieux': User.objects.annotate(
num=Count('whitelist')
).order_by('-num')[:10],
'Droits': User.objects.annotate(
num=Count('groups')
).order_by('-num')[:10],
},
'Etablissement': {
'Utilisateur': School.objects.annotate(
num=Count('user')
).order_by('-num')[:10],
},
'Moyen de paiement': {
'Utilisateur': Paiement.objects.annotate(
num=Count('facture')
).order_by('-num')[:10],
},
'Banque': {
'Utilisateur': Banque.objects.annotate(
num=Count('facture')
).order_by('-num')[:10],
},
}
return render(request, 'logs/stats_users.html', {
'stats_list': stats,
'stats_dict': STATS_DICT,
'active_field': onglet
})
@login_required
@can_view_app('users')
def stats_actions(request):
"""Vue qui affiche les statistiques de modifications d'objets par
utilisateurs.
Affiche le nombre de modifications aggrégées par utilisateurs"""
stats = {
'Utilisateur': {
'Action': User.objects.annotate(
num=Count('revision')
).order_by('-num')[:40],
},
}
return render(request, 'logs/stats_users.html', {'stats_list': stats})
@login_required
@can_view_app('users')
def stats_droits(request):
"""Affiche la liste des droits et les users ayant chaque droit"""
depart=time()
stats_list={}
for droit in ListRight.objects.all().select_related('group_ptr'):
stats_list[droit]=droit.user_set.all().annotate(num=Count('revision'),last=Max('revision__date_created'))
return render(request, 'logs/stats_droits.html', {'stats_list': stats_list})