mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-23 23:43:47 +00:00
Merge branch 'new_upstream' into 'master'
New upstream See merge request federez/re2o!104
This commit is contained in:
commit
708d897ec8
62 changed files with 1743 additions and 174 deletions
|
@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if facture_list.paginator %}
|
||||
{% include "pagination.html" with list=facture_list %}
|
||||
{% endif %}
|
||||
|
@ -85,4 +85,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% if facture_list.paginator %}
|
||||
{% include "pagination.html" with list=facture_list %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -192,19 +192,21 @@ def post_auth(data):
|
|||
|
||||
mac = data.get('Calling-Station-Id', None)
|
||||
|
||||
# Switch et bornes héritent de machine et peuvent avoir plusieurs interfaces filles
|
||||
nas_machine = nas_instance.machine
|
||||
# Si il s'agit d'un switch
|
||||
if hasattr(nas_instance, 'switch'):
|
||||
if hasattr(nas_machine, 'switch'):
|
||||
port = data.get('NAS-Port-Id', data.get('NAS-Port', None))
|
||||
#Pour les infrastructures possédant des switchs Juniper :
|
||||
#On vérifie si le switch fait partie d'un stack Juniper
|
||||
instance_stack = nas_instance.switch.stack
|
||||
instance_stack = nas_machine.switch.stack
|
||||
if instance_stack:
|
||||
# Si c'est le cas, on resélectionne le bon switch dans la stack
|
||||
id_stack_member = port.split("-")[1].split('/')[0]
|
||||
nas_instance = Interface.objects.filter(switch__in=Switch.objects.filter(stack=instance_stack).filter(stack_member_id=id_stack_member)).select_related('domain__extension').first()
|
||||
nas_machine = Switch.objects.filter(stack=instance_stack).filter(stack_member_id=id_stack_member).prefetch_related('interface_set__domain__extension').first()
|
||||
# On récupère le numéro du port sur l'output de freeradius. La ligne suivante fonctionne pour cisco, HP et Juniper
|
||||
port = port.split(".")[0].split('/')[-1][-2:]
|
||||
out = decide_vlan_and_register_switch(nas_instance, nas_type, port, mac)
|
||||
out = decide_vlan_and_register_switch(nas_machine, nas_type, port, mac)
|
||||
sw_name, room, reason, vlan_id = out
|
||||
|
||||
log_message = '(fil) %s -> %s [%s%s]' % \
|
||||
|
@ -271,7 +273,7 @@ def check_user_machine_and_register(nas_type, username, mac_address):
|
|||
return (False, u"Machine inconnue", '')
|
||||
|
||||
|
||||
def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
|
||||
def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_address):
|
||||
"""Fonction de placement vlan pour un switch en radius filaire auth par mac.
|
||||
Plusieurs modes :
|
||||
- nas inconnu, port inconnu : on place sur le vlan par defaut VLAN_OK
|
||||
|
@ -296,12 +298,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
|
|||
# Get port from switch and port number
|
||||
extra_log = ""
|
||||
# Si le NAS est inconnu, on place sur le vlan defaut
|
||||
if not nas:
|
||||
if not nas_machine:
|
||||
return ('?', u'Chambre inconnue', u'Nas inconnu', VLAN_OK)
|
||||
|
||||
sw_name = str(nas)
|
||||
sw_name = str(nas_machine)
|
||||
|
||||
port = Port.objects.filter(switch=Switch.objects.filter(switch_interface=nas), port=port_number).first()
|
||||
port = Port.objects.filter(switch=Switch.objects.filter(machine_ptr=nas_machine), port=port_number).first()
|
||||
#Si le port est inconnu, on place sur le vlan defaut
|
||||
if not port:
|
||||
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
||||
|
|
82
logs/templates/logs/aff_stats_droits.html
Normal file
82
logs/templates/logs/aff_stats_droits.html
Normal file
|
@ -0,0 +1,82 @@
|
|||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% for droit,users in stats_list.items %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading clearfix" data-parent="#accordion" data-toggle="collapse" data-target="#collapse{{droit.id}}">
|
||||
<h2 class="panel-title pull-left">
|
||||
<i class="fa fa-address-book"></i>
|
||||
{{droit}}
|
||||
<span class="badge">{{users.count}}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-collapse collapse" id="collapse{{droit.id}}">
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pseudo</th>
|
||||
<th>Adhésion</th>
|
||||
<th>Derniere connexion</th>
|
||||
<th>Nombre d'actions</th>
|
||||
<th>Date de la dernière action</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for utilisateur in users %}
|
||||
<tr>
|
||||
<td>{{ utilisateur.pseudo }}</td>
|
||||
{% if utilisateur.is_adherent %}
|
||||
<td><p class="text-success">Adhérent</p></td>
|
||||
{% elif not utilisateur.end_adhesion %}
|
||||
<td><p class="text-warning">On ne s'en souvient plus...</p></td>
|
||||
{% else %}
|
||||
<td><p class="text-danger">Plus depuis {{ utilisateur.end_adhesion }}</p></td>
|
||||
{% endif %}
|
||||
<td>{{ utilisateur.last_login }}</td>
|
||||
<td>{{ utilisateur.num }}</td>
|
||||
{% if not utilisateur.last %}
|
||||
<td><p class="text-danger">Jamais</p></td>
|
||||
{% else %}
|
||||
<td><p class="text-success">{{utilisateur.last}}</p></td>
|
||||
{% endif %}
|
||||
<td>
|
||||
<a href="{% url 'users:del-group' utilisateur.id droit.id %}">
|
||||
<button type="button" class="btn btn-danger" aria-label="Left Align">
|
||||
<span class="fa fa-user-times" aria-hidden="true"></span>
|
||||
</button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
|
@ -51,5 +51,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<i class="fa fa-users"></i>
|
||||
Utilisateurs
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "logs:stats-droits" %}">
|
||||
<i class="fa fa-balance-scale"></i>
|
||||
Groupes de droit
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% endblock %}
|
||||
|
|
36
logs/templates/logs/stats_droits.html
Normal file
36
logs/templates/logs/stats_droits.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends "logs/sidebar.html" %}
|
||||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Statistiques des droits{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Statistiques des droits</h2>
|
||||
{% include "logs/aff_stats_droits.html" with stats_list=stats_list %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
|
@ -39,4 +39,5 @@ urlpatterns = [
|
|||
url(r'^stats_models/$', views.stats_models, name='stats-models'),
|
||||
url(r'^stats_users/$', views.stats_users, name='stats-users'),
|
||||
url(r'^stats_actions/$', views.stats_actions, name='stats-actions'),
|
||||
url(r'^stats_droits/$', views.stats_droits, name='stats-droits'),
|
||||
]
|
||||
|
|
|
@ -42,11 +42,13 @@ 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
|
||||
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,
|
||||
|
@ -446,3 +448,15 @@ def stats_actions(request):
|
|||
},
|
||||
}
|
||||
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})
|
||||
|
|
|
@ -23,11 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if machines_list.paginator %}
|
||||
{% include "pagination.html" with list=machines_list %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<table class="table" id="machines_table">
|
||||
<colgroup>
|
||||
<col>
|
||||
|
@ -175,6 +176,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
$("#machines_table").ready( function() {
|
||||
var alias_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.domain.related_domain.all %}$("#collapseDomain_{{interface.id}}"), {% endif %}{% endfor %}{% endfor %}];
|
||||
|
@ -193,3 +195,4 @@ $("#machines_table").ready( function() {
|
|||
{% if machines_list.paginator %}
|
||||
{% include "pagination.html" with list=machines_list %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
20
preferences/migrations/0031_auto_20180323_0218.py
Normal file
20
preferences/migrations/0031_auto_20180323_0218.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('preferences', '0030_merge_20180320_1419'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='generaloption',
|
||||
name='email_from',
|
||||
field=models.EmailField(default='www-data@example.com', max_length=254),
|
||||
),
|
||||
]
|
22
preferences/migrations/0032_optionaluser_shell_default.py
Normal file
22
preferences/migrations/0032_optionaluser_shell_default.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-24 19:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0070_auto_20180324_1906'),
|
||||
('preferences', '0031_auto_20180323_0218'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='optionaluser',
|
||||
name='shell_default',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.ListShell'),
|
||||
),
|
||||
]
|
|
@ -89,6 +89,12 @@ class OptionalUser(PreferencesModel):
|
|||
default=False,
|
||||
help_text="Un nouvel utilisateur peut se créer son compte sur re2o"
|
||||
)
|
||||
shell_default = models.OneToOneField(
|
||||
'users.ListShell',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
|
|
|
@ -68,7 +68,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<th>Auto inscription</th>
|
||||
<td>{{ useroptions.self_adhesion }}</td>
|
||||
</tr>
|
||||
<th>Shell par défaut des utilisateurs</th>
|
||||
<td>{{ useroptions.shell_default }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h4>Préférences machines</h4>
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'OptionalMachine' %}">
|
||||
|
|
|
@ -37,8 +37,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% for service in service_list %}
|
||||
<div class="col-12">
|
||||
<div class="thumbnail">
|
||||
<img src="{% static service.image %}" alt="{{ service.name }}">
|
||||
<div class="caption">
|
||||
<a href="{{ service.url }}"><img src="{% static service.image %}" alt="{{ service.name }}"></a>
|
||||
<div class="caption">
|
||||
<h3>{{ service.name }}</h3>
|
||||
<p>{{ service.description }}</p>
|
||||
<p><a href="{{ service.url }}" class="btn btn-primary" role="button">Accéder</a></p>
|
||||
|
|
|
@ -122,6 +122,7 @@ MODEL_NAME = {
|
|||
# topologie
|
||||
'Stack' : topologie.models.Stack,
|
||||
'Switch' : topologie.models.Switch,
|
||||
'AccessPoint' : topologie.models.AccessPoint,
|
||||
'ModelSwitch' : topologie.models.ModelSwitch,
|
||||
'ConstructorSwitch' : topologie.models.ConstructorSwitch,
|
||||
'Port' : topologie.models.Port,
|
||||
|
@ -133,6 +134,7 @@ MODEL_NAME = {
|
|||
'ServiceUser' : users.models.ServiceUser,
|
||||
'School' : users.models.School,
|
||||
'ListRight' : users.models.ListRight,
|
||||
'ListShell' : users.models.ListShell,
|
||||
'Ban' : users.models.Ban,
|
||||
'Whitelist' : users.models.Whitelist,
|
||||
}
|
||||
|
|
|
@ -213,8 +213,8 @@ class SortTable:
|
|||
'default': ['-date']
|
||||
}
|
||||
TOPOLOGIE_INDEX = {
|
||||
'switch_dns': ['switch_interface__domain__name'],
|
||||
'switch_ip': ['switch_interface__ipv4__ipv4'],
|
||||
'switch_dns': ['interface__domain__name'],
|
||||
'switch_ip': ['interface__ipv4__ipv4'],
|
||||
'switch_loc': ['location'],
|
||||
'switch_ports': ['number'],
|
||||
'switch_stack': ['stack__name'],
|
||||
|
@ -233,6 +233,12 @@ class SortTable:
|
|||
'room_name': ['name'],
|
||||
'default': ['name']
|
||||
}
|
||||
TOPOLOGIE_INDEX_BORNE = {
|
||||
'ap_name': ['interface__domain__name'],
|
||||
'ap_ip': ['interface__ipv4__ipv4'],
|
||||
'ap_mac': ['interface__mac_address'],
|
||||
'default': ['interface__domain__name']
|
||||
}
|
||||
TOPOLOGIE_INDEX_STACK = {
|
||||
'stack_name': ['name'],
|
||||
'stack_id': ['stack_id'],
|
||||
|
|
|
@ -65,6 +65,7 @@ HISTORY_BIND = {
|
|||
'school' : users.models.School,
|
||||
'listright' : users.models.ListRight,
|
||||
'serviceuser' : users.models.ServiceUser,
|
||||
'shell' : users.models.ListShell,
|
||||
},
|
||||
'preferences' : {
|
||||
'service' : preferences.models.Service,
|
||||
|
@ -82,6 +83,7 @@ HISTORY_BIND = {
|
|||
'stack' : topologie.models.Stack,
|
||||
'model_switch' : topologie.models.ModelSwitch,
|
||||
'constructor_switch' : topologie.models.ConstructorSwitch,
|
||||
'ap' : topologie.models.AccessPoint,
|
||||
},
|
||||
'machines' : {
|
||||
'machine' : machines.models.Machine,
|
||||
|
|
|
@ -260,7 +260,7 @@ def search_single_word(word, filters, user,
|
|||
) | Q(
|
||||
machine_interface__domain__name__icontains=word
|
||||
) | Q(
|
||||
related__switch__switch_interface__domain__name__icontains=word
|
||||
related__switch__domain__name__icontains=word
|
||||
) | Q(
|
||||
radius__icontains=word
|
||||
) | Q(
|
||||
|
@ -277,9 +277,9 @@ def search_single_word(word, filters, user,
|
|||
# Switches
|
||||
if '7' in aff and Switch.can_view_all(user):
|
||||
filter_switches = Q(
|
||||
switch_interface__domain__name__icontains=word
|
||||
domain__name__icontains=word
|
||||
) | Q(
|
||||
switch_interface__ipv4__ipv4__icontains=word
|
||||
ipv4__ipv4__icontains=word
|
||||
) | Q(
|
||||
location__icontains=word
|
||||
) | Q(
|
||||
|
|
|
@ -45,7 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<p><form method="post" action="{% url 'login' %}">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_button "Login" button_type="submit" icon="log-in" %}
|
||||
<button class="btn btn-success" type="submit"><span class="glyphicon glyphicon-log-in"></span> Login</button>
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
</form></p>
|
||||
<p><a class="btn btn-warning btn-sm" role="button" href="{% url 'users:reset-password' %}"> Mot de passe oublié ?</a></p>
|
||||
|
|
|
@ -29,7 +29,15 @@ from __future__ import unicode_literals
|
|||
from django.contrib import admin
|
||||
from reversion.admin import VersionAdmin
|
||||
|
||||
from .models import Port, Room, Switch, Stack, ModelSwitch, ConstructorSwitch
|
||||
from .models import (
|
||||
Port,
|
||||
Room,
|
||||
Switch,
|
||||
Stack,
|
||||
ModelSwitch,
|
||||
ConstructorSwitch,
|
||||
AccessPoint
|
||||
)
|
||||
|
||||
|
||||
class StackAdmin(VersionAdmin):
|
||||
|
@ -47,6 +55,11 @@ class PortAdmin(VersionAdmin):
|
|||
pass
|
||||
|
||||
|
||||
class AccessPointAdmin(VersionAdmin):
|
||||
"""Administration d'une borne"""
|
||||
pass
|
||||
|
||||
|
||||
class RoomAdmin(VersionAdmin):
|
||||
"""Administration d'un chambre"""
|
||||
pass
|
||||
|
@ -63,6 +76,7 @@ class ConstructorSwitchAdmin(VersionAdmin):
|
|||
|
||||
|
||||
admin.site.register(Port, PortAdmin)
|
||||
admin.site.register(AccessPoint, AccessPointAdmin)
|
||||
admin.site.register(Room, RoomAdmin)
|
||||
admin.site.register(Switch, SwitchAdmin)
|
||||
admin.site.register(Stack, StackAdmin)
|
||||
|
|
|
@ -33,9 +33,23 @@ NewSwitchForm)
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from machines.models import Interface
|
||||
from machines.forms import (
|
||||
EditInterfaceForm,
|
||||
EditMachineForm,
|
||||
NewMachineForm
|
||||
)
|
||||
from django import forms
|
||||
from django.forms import ModelForm, Form
|
||||
from .models import Port, Switch, Room, Stack, ModelSwitch, ConstructorSwitch
|
||||
from django.db.models import Prefetch
|
||||
from .models import (
|
||||
Port,
|
||||
Switch,
|
||||
Room,
|
||||
Stack,
|
||||
ModelSwitch,
|
||||
ConstructorSwitch,
|
||||
AccessPoint
|
||||
)
|
||||
|
||||
|
||||
class PortForm(ModelForm):
|
||||
|
@ -67,10 +81,12 @@ class EditPortForm(ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['machine_interface'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
.select_related('domain__extension')
|
||||
self.fields['related'].queryset = Port.objects.all()\
|
||||
.select_related('switch__switch_interface__domain__extension')\
|
||||
.order_by('switch', 'port')
|
||||
.prefetch_related(Prefetch(
|
||||
'switch__interface_set',
|
||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
||||
))
|
||||
|
||||
|
||||
class AddPortForm(ModelForm):
|
||||
|
@ -84,10 +100,12 @@ class AddPortForm(ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['machine_interface'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
.select_related('domain__extension')
|
||||
self.fields['related'].queryset = Port.objects.all()\
|
||||
.select_related('switch__switch_interface__domain__extension')\
|
||||
.order_by('switch', 'port')
|
||||
.prefetch_related(Prefetch(
|
||||
'switch__interface_set',
|
||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
||||
))
|
||||
|
||||
|
||||
class StackForm(ModelForm):
|
||||
|
@ -102,32 +120,34 @@ class StackForm(ModelForm):
|
|||
super(StackForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class EditSwitchForm(ModelForm):
|
||||
class AddAccessPointForm(NewMachineForm):
|
||||
"""Formulaire pour la création d'une borne
|
||||
Relié directement au modèle borne"""
|
||||
class Meta:
|
||||
model = AccessPoint
|
||||
fields = ['location', 'name']
|
||||
|
||||
|
||||
class EditAccessPointForm(EditMachineForm):
|
||||
"""Edition d'une borne. Edition complète"""
|
||||
class Meta:
|
||||
model = AccessPoint
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class EditSwitchForm(EditMachineForm):
|
||||
"""Permet d'éditer un switch : nom et nombre de ports"""
|
||||
class Meta:
|
||||
model = Switch
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(EditSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['switch_interface'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
self.fields['location'].label = 'Localisation'
|
||||
self.fields['number'].label = 'Nombre de ports'
|
||||
|
||||
|
||||
class NewSwitchForm(ModelForm):
|
||||
class NewSwitchForm(NewMachineForm):
|
||||
"""Permet de créer un switch : emplacement, paramètres machine,
|
||||
membre d'un stack (option), nombre de ports (number)"""
|
||||
class Meta(EditSwitchForm.Meta):
|
||||
fields = ['location', 'number', 'details', 'stack', 'stack_member_id']
|
||||
fields = ['name', 'location', 'number', 'stack', 'stack_member_id']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(NewSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['location'].label = 'Localisation'
|
||||
self.fields['number'].label = 'Nombre de ports'
|
||||
|
||||
class EditRoomForm(ModelForm):
|
||||
"""Permet d'éediter le nom et commentaire d'une prise murale"""
|
||||
|
|
47
topologie/management/commands/sync_unifi_ap.py
Normal file
47
topologie/management/commands/sync_unifi_ap.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# ⁻*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Gabriel Detraz
|
||||
#
|
||||
# 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.
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from pymongo import MongoClient
|
||||
from topologie.models import Borne
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Ce script donne un nom aux bornes dans le controleur unifi.
|
||||
A lancer sur le serveur en local où se trouve le controleur'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
# Connexion mongodb
|
||||
client = MongoClient("mongodb://localhost:27117")
|
||||
db = client.ace
|
||||
device = db['device']
|
||||
|
||||
bornes = Borne.objects.all()
|
||||
|
||||
def set_bornes_names(liste_bornes):
|
||||
"""Met à jour les noms des bornes dans la bdd du controleur"""
|
||||
for borne in liste_bornes:
|
||||
if borne.ipv4 and borne.domain:
|
||||
device.find_one_and_update({'ip': str(borne.ipv4)}, {'$set': {'name': str(borne.domain.name)}})
|
||||
return
|
||||
|
||||
set_bornes_names(bornes)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Mise à jour de la base de donnée unifi avec succès'))
|
28
topologie/migrations/0034_borne.py
Normal file
28
topologie/migrations/0034_borne.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0076_auto_20180130_1623'),
|
||||
('topologie', '0033_auto_20171231_1743'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Borne',
|
||||
fields=[
|
||||
('interface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Interface')),
|
||||
('location', models.CharField(help_text="Détails sur la localisation de l'AP", max_length=255)),
|
||||
],
|
||||
options={
|
||||
'permissions': (('view_borne', 'Peut voir une borne'),),
|
||||
},
|
||||
bases=('machines.interface',),
|
||||
),
|
||||
]
|
20
topologie/migrations/0035_auto_20180324_0023.py
Normal file
20
topologie/migrations/0035_auto_20180324_0023.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 23:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0034_borne'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='borne',
|
||||
name='location',
|
||||
field=models.CharField(blank=True, help_text="Détails sur la localisation de l'AP", max_length=255, null=True),
|
||||
),
|
||||
]
|
32
topologie/migrations/0036_transferborne.py
Normal file
32
topologie/migrations/0036_transferborne.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0035_auto_20180324_0023'),
|
||||
]
|
||||
|
||||
def transfer_bornes(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
machinetype = apps.get_model("machines", "MachineType")
|
||||
borne = apps.get_model("topologie", "Borne")
|
||||
interface = apps.get_model("machines", "Interface")
|
||||
bornes_list = machinetype.objects.using(db_alias).filter(type__icontains='borne')
|
||||
if bornes_list:
|
||||
for inter in interface.objects.using(db_alias).filter(type=bornes_list.first()):
|
||||
borne_object = borne()
|
||||
borne_object.interface_ptr_id = inter.pk
|
||||
borne_object.__dict__.update(inter.__dict__)
|
||||
borne_object.save()
|
||||
|
||||
def untransfer_bornes(apps, schema_editor):
|
||||
return
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_bornes, untransfer_bornes),
|
||||
]
|
33
topologie/migrations/0037_auto_20180325_0127.py
Normal file
33
topologie/migrations/0037_auto_20180325_0127.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 00:27
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0076_auto_20180130_1623'),
|
||||
('topologie', '0036_transferborne'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NewSwitch',
|
||||
fields=[
|
||||
('interface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Interface')),
|
||||
('location', models.CharField(max_length=255)),
|
||||
('number', models.PositiveIntegerField()),
|
||||
('stack_member_id', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch')),
|
||||
('stack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack')),
|
||||
],
|
||||
bases=('machines.interface',),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='newswitch',
|
||||
unique_together=set([('stack', 'stack_member_id')]),
|
||||
),
|
||||
]
|
37
topologie/migrations/0038_transfersw.py
Normal file
37
topologie/migrations/0038_transfersw.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0037_auto_20180325_0127'),
|
||||
]
|
||||
|
||||
def transfer_sw(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
newswitch = apps.get_model("topologie", "NewSwitch")
|
||||
switch = apps.get_model("topologie", "Switch")
|
||||
interface = apps.get_model("machines", "Interface")
|
||||
sw_list = switch.objects.using(db_alias).all()
|
||||
for sw in sw_list:
|
||||
new_sw = newswitch()
|
||||
new_sw.location = sw.location
|
||||
new_sw.number = sw.number
|
||||
new_sw.details = sw.details
|
||||
new_sw.stack = sw.stack
|
||||
new_sw.stack_member_id = sw.stack_member_id
|
||||
new_sw.model = sw.model
|
||||
new_sw.interface_ptr_id = sw.switch_interface.pk
|
||||
new_sw.__dict__.update(sw.switch_interface.__dict__)
|
||||
new_sw.save()
|
||||
|
||||
def untransfer_sw(apps, schema_editor):
|
||||
return
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_sw, untransfer_sw),
|
||||
]
|
21
topologie/migrations/0039_port_new_switch.py
Normal file
21
topologie/migrations/0039_port_new_switch.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 00:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0038_transfersw'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='port',
|
||||
name='new_switch',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.NewSwitch'),
|
||||
),
|
||||
]
|
28
topologie/migrations/0040_transferports.py
Normal file
28
topologie/migrations/0040_transferports.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0039_port_new_switch'),
|
||||
]
|
||||
|
||||
def transfer_port(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
port = apps.get_model("topologie", "Port")
|
||||
switch = apps.get_model("topologie", "NewSwitch")
|
||||
port_list = port.objects.using(db_alias).all()
|
||||
for p in port_list:
|
||||
p.new_switch = switch.objects.filter(interface_ptr=p.switch.switch_interface).first()
|
||||
p.save()
|
||||
|
||||
def untransfer_port(apps, schema_editor):
|
||||
return
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_port, untransfer_port),
|
||||
]
|
24
topologie/migrations/0041_transferportsw.py
Normal file
24
topologie/migrations/0041_transferportsw.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0040_transferports'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='port',
|
||||
unique_together=set([]),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='port',
|
||||
name='switch',
|
||||
),
|
||||
migrations.RenameField('Port', 'new_switch', 'switch')
|
||||
]
|
18
topologie/migrations/0042_transferswitch.py
Normal file
18
topologie/migrations/0042_transferswitch.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0041_transferportsw'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='Switch',
|
||||
),
|
||||
]
|
16
topologie/migrations/0043_renamenewswitch.py
Normal file
16
topologie/migrations/0043_renamenewswitch.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0042_transferswitch'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(old_name='NewSwitch', new_name='Switch'),
|
||||
]
|
28
topologie/migrations/0044_auto_20180326_0002.py
Normal file
28
topologie/migrations/0044_auto_20180326_0002.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 22:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0043_renamenewswitch'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='Borne',
|
||||
new_name='AccessPoint',
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='accesspoint',
|
||||
options={'permissions': (('view_ap', 'Peut voir une borne'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='switch',
|
||||
options={'permissions': (('view_switch', 'Peut voir un objet switch'),)},
|
||||
),
|
||||
]
|
21
topologie/migrations/0045_auto_20180326_0123.py
Normal file
21
topologie/migrations/0045_auto_20180326_0123.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 23:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0044_auto_20180326_0002'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='port',
|
||||
name='switch',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.Switch'),
|
||||
),
|
||||
]
|
19
topologie/migrations/0046_auto_20180326_0129.py
Normal file
19
topologie/migrations/0046_auto_20180326_0129.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 23:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0045_auto_20180326_0123'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='port',
|
||||
unique_together=set([('switch', 'port')]),
|
||||
),
|
||||
]
|
26
topologie/migrations/0047_ap_machine.py
Normal file
26
topologie/migrations/0047_ap_machine.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0046_auto_20180326_0129'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NewAccessPoint',
|
||||
fields=[
|
||||
('machine_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Machine')),
|
||||
('location', models.CharField(help_text="Détails sur la localisation de l'AP", max_length=255, null=True, blank=True)),
|
||||
],
|
||||
bases=('machines.machine',),
|
||||
),
|
||||
]
|
39
topologie/migrations/0048_ap_machine.py
Normal file
39
topologie/migrations/0048_ap_machine.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0047_ap_machine'),
|
||||
]
|
||||
|
||||
def transfer_ap(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
ap = apps.get_model("topologie", "AccessPoint")
|
||||
new_ap = apps.get_model("topologie", "NewAccessPoint")
|
||||
ap_list = ap.objects.using(db_alias).all()
|
||||
for borne in ap_list:
|
||||
new_borne = new_ap()
|
||||
new_borne.machine_ptr_id = borne.machine.pk
|
||||
new_borne.__dict__.update(borne.machine.__dict__)
|
||||
new_borne.location = borne.location
|
||||
new_borne.save()
|
||||
|
||||
def untransfer_ap(apps, schema_editor):
|
||||
return
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_ap, untransfer_ap),
|
||||
migrations.DeleteModel(
|
||||
name='AccessPoint',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='NewAccessPoint',
|
||||
new_name='AccessPoint',
|
||||
),
|
||||
]
|
30
topologie/migrations/0049_switchs_machine.py
Normal file
30
topologie/migrations/0049_switchs_machine.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0048_ap_machine'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NewSw',
|
||||
fields=[
|
||||
('machine_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='machines.Machine')),
|
||||
('location', models.CharField(max_length=255)),
|
||||
('number', models.PositiveIntegerField()),
|
||||
('stack_member_id', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch')),
|
||||
('stack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack')),
|
||||
],
|
||||
bases=('machines.machine',),
|
||||
),
|
||||
]
|
21
topologie/migrations/0050_port_new_switch.py
Normal file
21
topologie/migrations/0050_port_new_switch.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-25 00:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0049_switchs_machine'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='port',
|
||||
name='new_sw',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.NewSw'),
|
||||
),
|
||||
]
|
39
topologie/migrations/0051_switchs_machine.py
Normal file
39
topologie/migrations/0051_switchs_machine.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0050_port_new_switch'),
|
||||
]
|
||||
|
||||
def transfer_sw(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
newswitch = apps.get_model("topologie", "NewSw")
|
||||
switch = apps.get_model("topologie", "Switch")
|
||||
machine = apps.get_model("machines", "Machine")
|
||||
sw_list = switch.objects.using(db_alias).all()
|
||||
for sw in sw_list:
|
||||
new_sw = newswitch()
|
||||
new_sw.location = sw.location
|
||||
new_sw.number = sw.number
|
||||
new_sw.details = sw.details
|
||||
new_sw.stack = sw.stack
|
||||
new_sw.stack_member_id = sw.stack_member_id
|
||||
new_sw.model = sw.model
|
||||
new_sw.machine_ptr_id = sw.interface_ptr.machine.pk
|
||||
new_sw.__dict__.update(sw.interface_ptr.machine.__dict__)
|
||||
new_sw.save()
|
||||
|
||||
def untransfer_sw(apps, schema_editor):
|
||||
return
|
||||
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(transfer_sw, untransfer_sw),
|
||||
]
|
37
topologie/migrations/0052_transferports.py
Normal file
37
topologie/migrations/0052_transferports.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-12-31 19:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0051_switchs_machine'),
|
||||
]
|
||||
|
||||
def transfer_port(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
port = apps.get_model("topologie", "Port")
|
||||
switch = apps.get_model("topologie", "NewSw")
|
||||
port_list = port.objects.using(db_alias).all()
|
||||
for p in port_list:
|
||||
p.new_sw = switch.objects.filter(machine_ptr=p.switch.machine).first()
|
||||
p.save()
|
||||
|
||||
def untransfer_port(apps, schema_editor):
|
||||
return
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='port',
|
||||
unique_together=set([]),
|
||||
),
|
||||
migrations.RunPython(transfer_port, untransfer_port),
|
||||
migrations.RemoveField(
|
||||
model_name='port',
|
||||
name='switch',
|
||||
),
|
||||
migrations.RenameField('Port', 'new_sw', 'switch')
|
||||
]
|
25
topologie/migrations/0053_finalsw.py
Normal file
25
topologie/migrations/0053_finalsw.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-23 01:18
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0052_transferports'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='Switch',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='NewSw',
|
||||
new_name='Switch',
|
||||
),
|
||||
]
|
38
topologie/migrations/0054_auto_20180326_1742.py
Normal file
38
topologie/migrations/0054_auto_20180326_1742.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-26 15:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('topologie', '0053_finalsw'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='accesspoint',
|
||||
options={'permissions': (('view_ap', 'Peut voir une borne'),)},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='switch',
|
||||
options={'permissions': (('view_switch', 'Peut voir un objet switch'),)},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='port',
|
||||
name='switch',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.Switch'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='port',
|
||||
unique_together=set([('switch', 'port')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='switch',
|
||||
unique_together=set([('stack', 'stack_member_id')]),
|
||||
),
|
||||
]
|
|
@ -47,6 +47,7 @@ from django.db import IntegrityError
|
|||
from django.db import transaction
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from machines.models import Machine, Interface
|
||||
|
||||
class Stack(models.Model):
|
||||
"""Un objet stack. Regrouppe des switchs en foreign key
|
||||
|
@ -108,7 +109,54 @@ class Stack(models.Model):
|
|||
inférieure à l'id minimale"})
|
||||
|
||||
|
||||
class Switch(models.Model):
|
||||
class AccessPoint(Machine):
|
||||
"""Define a wireless AP. Inherit from machines.interfaces
|
||||
|
||||
Definition pour une borne wifi , hérite de machines.interfaces
|
||||
"""
|
||||
PRETTY_NAME = "Borne WiFi"
|
||||
|
||||
location = models.CharField(
|
||||
max_length=255,
|
||||
help_text="Détails sur la localisation de l'AP",
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_ap", "Peut voir une borne"),
|
||||
)
|
||||
|
||||
def get_instance(ap_id, *args, **kwargs):
|
||||
return AccessPoint.objects.get(pk=ap_id)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
return user_request.has_perm('topologie.add_ap') , u"Vous n'avez pas le droit\
|
||||
de créer une borne"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('topologie.change_ap'):
|
||||
return False, u"Vous n'avez pas le droit d'éditer des bornes"
|
||||
return True, None
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('topologie.delete_ap'):
|
||||
return False, u"Vous n'avez pas le droit de supprimer une borne"
|
||||
return True, None
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('topologie.view_ap'):
|
||||
return False, u"Vous n'avez pas le droit de voir les bornes"
|
||||
return True, None
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
if not user_request.has_perm('topologie.view_ap'):
|
||||
return False, u"Vous n'avez pas le droit de voir les bornes"
|
||||
return True, None
|
||||
|
||||
|
||||
class Switch(Machine):
|
||||
""" Definition d'un switch. Contient un nombre de ports (number),
|
||||
un emplacement (location), un stack parent (optionnel, stack)
|
||||
et un id de membre dans le stack (stack_member_id)
|
||||
|
@ -122,13 +170,9 @@ class Switch(models.Model):
|
|||
id_max de la stack parente"""
|
||||
PRETTY_NAME = "Switch / Commutateur"
|
||||
|
||||
switch_interface = models.OneToOneField(
|
||||
'machines.Interface',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
location = models.CharField(max_length=255)
|
||||
number = models.PositiveIntegerField()
|
||||
details = models.CharField(max_length=255, blank=True)
|
||||
stack = models.ForeignKey(
|
||||
'topologie.Stack',
|
||||
blank=True,
|
||||
|
@ -176,11 +220,10 @@ class Switch(models.Model):
|
|||
return False, u"Vous n'avez pas le droit de voir les switch"
|
||||
return True, None
|
||||
|
||||
def __str__(self):
|
||||
return self.location + ' ' + str(self.switch_interface)
|
||||
|
||||
def clean(self):
|
||||
""" Verifie que l'id stack est dans le bon range"""
|
||||
""" Verifie que l'id stack est dans le bon range
|
||||
Appelle également le clean de la classe parente"""
|
||||
super(Switch, self).clean()
|
||||
if self.stack is not None:
|
||||
if self.stack_member_id is not None:
|
||||
if (self.stack_member_id > self.stack.member_id_max) or\
|
||||
|
@ -192,6 +235,7 @@ class Switch(models.Model):
|
|||
else:
|
||||
raise ValidationError({'stack_member_id': "L'id dans la stack\
|
||||
ne peut être nul"})
|
||||
|
||||
def create_ports(self, begin, end):
|
||||
""" Crée les ports de begin à end si les valeurs données sont cohérentes. """
|
||||
|
||||
|
@ -219,6 +263,9 @@ class Switch(models.Model):
|
|||
except IntegrityError:
|
||||
ValidationError("Création d'un port existant.")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.interface_set.first())
|
||||
|
||||
|
||||
class ModelSwitch(models.Model):
|
||||
"""Un modèle (au sens constructeur) de switch"""
|
||||
|
@ -372,11 +419,11 @@ class Port(models.Model):
|
|||
|
||||
def get_instance(port_id, *args, **kwargs):
|
||||
return Port.objects\
|
||||
.select_related('switch__switch_interface__domain__extension')\
|
||||
.select_related('machine_interface__domain__extension')\
|
||||
.select_related('machine_interface__switch')\
|
||||
.select_related('machine_interface__machine__switch')\
|
||||
.select_related('room')\
|
||||
.select_related('related')\
|
||||
.prefetch_related('switch__interface_set__domain__extension')\
|
||||
.get(pk=port_id)
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
|
|
74
topologie/templates/topologie/aff_ap.html
Normal file
74
topologie/templates/topologie/aff_ap.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load acl %}
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if ap_list.paginator %}
|
||||
{% include "pagination.html" with list=ap_list %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% include "buttons/sort.html" with prefix='ap' col='name' text='Borne' %}</th>
|
||||
<th>{% include "buttons/sort.html" with prefix='ap' col='mac' text='Addresse mac' %}</th>
|
||||
<th>{% include "buttons/sort.html" with prefix='ap' col='ip' text='Ipv4' %}</th>
|
||||
<th>Commentaire</th>
|
||||
<th>Localisation</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for ap in ap_list %}
|
||||
<tr>
|
||||
<td>{{ap.interface_set.first}}</td>
|
||||
<td>{{ap.interface_set.first.mac_address}}</td>
|
||||
<td>{{ap.interface_set.first.ipv4}}</td>
|
||||
<td>{{ap.interface_set.first.details}}</td>
|
||||
<td>{{ap.location}}</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'ap' ap.pk %}">
|
||||
<i class="fa fa-history"></i>
|
||||
</a>
|
||||
{% can_edit ap %}
|
||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-ap' ap.id %}">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_delete ap %}
|
||||
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'machines:del-machine' ap.id %}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
{% acl_end %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
||||
{% if ap_list.paginator %}
|
||||
{% include "pagination.html" with list=ap_list %}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -46,23 +46,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
<td>
|
||||
<a title="Configuer" href="{% url 'topologie:index-port' switch.pk %}">
|
||||
{{switch.switch_interface}}
|
||||
{{switch}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{switch.switch_interface.ipv4}}</td>
|
||||
<td>{{switch.interface_set.first.ipv4}}</td>
|
||||
<td>{{switch.location}}</td>
|
||||
<td>{{switch.number}}</td>
|
||||
<td>{{switch.stack.name}}</td>
|
||||
<td>{{switch.stack_member_id}}</td>
|
||||
<td>{{switch.model}}</td>
|
||||
<td>{{switch.details}}</td>
|
||||
<td>{{switch.interface_set.first.details}}</td>
|
||||
<td class="text-right">
|
||||
{% include 'buttons/history.html' with href='topologie:history' name='switch' id=switch.pk%}
|
||||
{% can_edit switch %}
|
||||
{% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.pk %}
|
||||
{% acl_end %}
|
||||
{% can_delete switch %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-interface' id=switch.switch_interface.id %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-machine' id=switch.id %}
|
||||
{% acl_end %}
|
||||
{% can_create Port %}
|
||||
{% include 'buttons/add.html' with href='topologie:create-ports' id=switch.pk desc='Création de ports'%}
|
||||
|
|
41
topologie/templates/topologie/index_ap.html
Normal file
41
topologie/templates/topologie/index_ap.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "topologie/sidebar.html" %}
|
||||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Bornes WiFi{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Points d'accès WiFi</h2>
|
||||
{% can_create AccessPoint %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-ap' %}"><i class="fa fa-plus"></i> Ajouter une borne</a>
|
||||
<hr>
|
||||
{% acl_end %}
|
||||
{% include "topologie/aff_ap.html" with ap_list=ap_list %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
|
@ -26,12 +26,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
{% block sidebar %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-room" %}">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Chambres
|
||||
<i class="fa fa-home"></i>
|
||||
Chambres et locaux
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "topologie:index" %}">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
<i class="fa fa-microchip"></i>
|
||||
Switchs
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-ap" %}">
|
||||
<i class="fa fa-wifi"></i>
|
||||
Bornes WiFi
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-stack" %}">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
|
|
|
@ -32,44 +32,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% if topoform %}
|
||||
{% bootstrap_form_errors topoform %}
|
||||
{% endif %}
|
||||
{% if machineform %}
|
||||
{% bootstrap_form_errors machineform %}
|
||||
{% endif %}
|
||||
{% if interfaceform %}
|
||||
{% bootstrap_form_errors interfaceform %}
|
||||
{% endif %}
|
||||
{% if domainform %}
|
||||
{% bootstrap_form_errors domainform %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{% if id_switch %}
|
||||
<a class="btn btn-primary" href="{% url "topologie:index-port" id_switch %}" role="button">{% bootstrap_icon "list" %} Aller à la liste des ports</a>
|
||||
{% endif %}
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if topoform %}
|
||||
<h3>Réglage spécifiques du switch</h3>
|
||||
{% massive_bootstrap_form topoform 'switch_interface' %}
|
||||
{% endif %}
|
||||
{% if machineform %}
|
||||
<h3>Réglages généraux de la machine associée au switch</h3>
|
||||
{% massive_bootstrap_form machineform 'user' %}
|
||||
{% endif %}
|
||||
{% if interfaceform %}
|
||||
<h3>Réglages généraux de l'interface associée au switch</h3>
|
||||
{% if i_mbf_param %}
|
||||
{% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_mbf_param %}
|
||||
{% else %}
|
||||
{% massive_bootstrap_form interfaceform 'ipv4,machine' %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if domainform %}
|
||||
<h3>Nom de la machine</h3>
|
||||
{% bootstrap_form domainform %}
|
||||
{% endif %}
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %}
|
||||
{% bootstrap_button "Créer" button_type="submit" icon="ok" %}
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
|
|
63
topologie/templates/topologie/topo_more.html
Normal file
63
topologie/templates/topologie/topo_more.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
{% extends "topologie/sidebar.html" %}
|
||||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load massive_bootstrap_form %}
|
||||
|
||||
{% block title %}Création et modification d'un objet topologie{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if topoform %}
|
||||
{% bootstrap_form_errors topoform %}
|
||||
{% endif %}
|
||||
{% if machineform %}
|
||||
{% bootstrap_form_errors machineform %}
|
||||
{% endif %}
|
||||
{% if domainform %}
|
||||
{% bootstrap_form_errors domainform %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if topoform %}
|
||||
<h3>Réglage spécifiques du {{ device }}</h3>
|
||||
{% massive_bootstrap_form topoform 'ipv4,machine' mbf_param=i_mbf_param%}
|
||||
{% endif %}
|
||||
{% if machineform %}
|
||||
<h3>Réglages généraux de la machine associée au {{ device }}</h3>
|
||||
{% massive_bootstrap_form machineform 'user' %}
|
||||
{% endif %}
|
||||
{% if domainform %}
|
||||
<h3>Nom de la machine</h3>
|
||||
{% bootstrap_form domainform %}
|
||||
{% endif %}
|
||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %}
|
||||
</form>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
|
@ -35,7 +35,11 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index'),
|
||||
url(r'^new_switch/$', views.new_switch, name='new-switch'),
|
||||
url(r'^index_ap/$', views.index_ap, name='index-ap'),
|
||||
url(r'^new_ap/$', views.new_ap, name='new-ap'),
|
||||
url(r'^edit_ap/(?P<ap_id>[0-9]+)$',
|
||||
views.edit_ap,
|
||||
name='edit-ap'),
|
||||
url(r'^create_ports/(?P<switch_id>[0-9]+)$',
|
||||
views.create_ports,
|
||||
name='create-ports'),
|
||||
|
@ -43,6 +47,7 @@ urlpatterns = [
|
|||
url(r'^new_room/$', views.new_room, name='new-room'),
|
||||
url(r'^edit_room/(?P<room_id>[0-9]+)$', views.edit_room, name='edit-room'),
|
||||
url(r'^del_room/(?P<room_id>[0-9]+)$', views.del_room, name='del-room'),
|
||||
url(r'^new_switch/$', views.new_switch, name='new-switch'),
|
||||
url(r'^switch/(?P<switch_id>[0-9]+)$',
|
||||
views.index_port,
|
||||
name='index-port'),
|
||||
|
|
|
@ -41,7 +41,7 @@ from django.contrib import messages
|
|||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.db import IntegrityError
|
||||
from django.db import transaction
|
||||
from django.db.models import ProtectedError
|
||||
from django.db.models import ProtectedError, Prefetch
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from reversion import revisions as reversion
|
||||
|
@ -53,7 +53,8 @@ from topologie.models import (
|
|||
Room,
|
||||
Stack,
|
||||
ModelSwitch,
|
||||
ConstructorSwitch
|
||||
ConstructorSwitch,
|
||||
AccessPoint
|
||||
)
|
||||
from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm
|
||||
from topologie.forms import (
|
||||
|
@ -62,7 +63,9 @@ from topologie.forms import (
|
|||
StackForm,
|
||||
EditModelSwitchForm,
|
||||
EditConstructorSwitchForm,
|
||||
CreatePortsForm
|
||||
CreatePortsForm,
|
||||
AddAccessPointForm,
|
||||
EditAccessPointForm
|
||||
)
|
||||
from users.views import form
|
||||
from re2o.utils import SortTable
|
||||
|
@ -81,6 +84,7 @@ from machines.forms import (
|
|||
AddInterfaceForm
|
||||
)
|
||||
from machines.views import generate_ipv4_mbf_param
|
||||
from machines.models import Interface
|
||||
from preferences.models import AssoOption, GeneralOption
|
||||
|
||||
|
||||
|
@ -89,9 +93,10 @@ from preferences.models import AssoOption, GeneralOption
|
|||
def index(request):
|
||||
""" Vue d'affichage de tous les swicthes"""
|
||||
switch_list = Switch.objects\
|
||||
.select_related('switch_interface__domain__extension')\
|
||||
.select_related('switch_interface__ipv4')\
|
||||
.select_related('switch_interface__domain')\
|
||||
.prefetch_related(Prefetch(
|
||||
'interface_set',
|
||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
||||
))\
|
||||
.select_related('stack')
|
||||
switch_list = SortTable.sort(
|
||||
switch_list,
|
||||
|
@ -124,9 +129,11 @@ def index_port(request, switch, switch_id):
|
|||
.select_related('room')\
|
||||
.select_related('machine_interface__domain__extension')\
|
||||
.select_related('machine_interface__machine__user')\
|
||||
.select_related(
|
||||
'related__switch__switch_interface__domain__extension'
|
||||
)\
|
||||
.select_related('related__switch')\
|
||||
.prefetch_related(Prefetch(
|
||||
'related__switch__interface_set',
|
||||
queryset=Interface.objects.select_related('domain__extension')
|
||||
))\
|
||||
.select_related('switch')
|
||||
port_list = SortTable.sort(
|
||||
port_list,
|
||||
|
@ -168,12 +175,43 @@ def index_room(request):
|
|||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@can_view_all(AccessPoint)
|
||||
def index_ap(request):
|
||||
""" Affichage de l'ensemble des bornes"""
|
||||
ap_list = AccessPoint.objects\
|
||||
.prefetch_related(Prefetch(
|
||||
'interface_set',
|
||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
||||
))
|
||||
ap_list = SortTable.sort(
|
||||
ap_list,
|
||||
request.GET.get('col'),
|
||||
request.GET.get('order'),
|
||||
SortTable.TOPOLOGIE_INDEX_BORNE
|
||||
)
|
||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||
paginator = Paginator(ap_list, pagination_number)
|
||||
page = request.GET.get('page')
|
||||
try:
|
||||
ap_list = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
# If page is not an integer, deliver first page.
|
||||
ap_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||
ap_list = paginator.page(paginator.num_pages)
|
||||
return render(request, 'topologie/index_ap.html', {
|
||||
'ap_list': ap_list
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@can_view_all(Stack)
|
||||
def index_stack(request):
|
||||
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
|
||||
stack_list = Stack.objects\
|
||||
.prefetch_related('switch_set__switch_interface__domain__extension')
|
||||
.prefetch_related('switch_set__domain__extension')
|
||||
stack_list = SortTable.sort(
|
||||
stack_list,
|
||||
request.GET.get('col'),
|
||||
|
@ -351,58 +389,53 @@ def new_switch(request):
|
|||
""" Creation d'un switch. Cree en meme temps l'interface et la machine
|
||||
associée. Vue complexe. Appelle successivement les 4 models forms
|
||||
adaptés : machine, interface, domain et switch"""
|
||||
switch = NewSwitchForm(request.POST or None)
|
||||
machine = NewMachineForm(
|
||||
switch = NewSwitchForm(
|
||||
request.POST or None,
|
||||
user=request.user
|
||||
)
|
||||
interface = AddInterfaceForm(
|
||||
request.POST or None,
|
||||
user=request.user
|
||||
)
|
||||
)
|
||||
domain = DomainForm(
|
||||
request.POST or None,
|
||||
)
|
||||
if switch.is_valid() and machine.is_valid() and interface.is_valid():
|
||||
if switch.is_valid() and interface.is_valid():
|
||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||
if not user:
|
||||
messages.error(request, "L'user association n'existe pas encore,\
|
||||
veuillez le créer ou le linker dans preferences")
|
||||
return redirect(reverse('topologie:index'))
|
||||
new_machine = machine.save(commit=False)
|
||||
new_machine.user = user
|
||||
new_interface = interface.save(commit=False)
|
||||
new_switch_instance = switch.save(commit=False)
|
||||
new_domain_instance = domain.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_machine.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_interface.machine = new_machine
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_domain_instance.interface_parent = new_interface
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_domain_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_switch_instance.switch_interface = new_interface
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_switch_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "Le switch a été créé")
|
||||
return redirect(reverse('topologie:index'))
|
||||
i_mbf_param = generate_ipv4_mbf_param( interface, False)
|
||||
new_switch = switch.save(commit=False)
|
||||
new_switch.user = user
|
||||
new_interface_instance = interface.save(commit=False)
|
||||
domain.instance.interface_parent = new_interface_instance
|
||||
if domain.is_valid():
|
||||
new_domain_instance = domain.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_switch.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_interface_instance.machine = new_switch
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_domain_instance.interface_parent = new_interface_instance
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_domain_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "Le switch a été créé")
|
||||
return redirect(reverse('topologie:index'))
|
||||
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
||||
return form({
|
||||
'topoform': switch,
|
||||
'machineform': machine,
|
||||
'interfaceform': interface,
|
||||
'topoform': interface,
|
||||
'machineform': switch,
|
||||
'domainform': domain,
|
||||
'i_mbf_param': i_mbf_param
|
||||
}, 'topologie/switch.html', request)
|
||||
'i_mbf_param': i_mbf_param,
|
||||
'device' : 'switch',
|
||||
}, 'topologie/topo_more.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -440,7 +473,6 @@ def create_ports(request, switch_id):
|
|||
'topologie:index-port',
|
||||
kwargs={'switch_id':switch_id}
|
||||
))
|
||||
|
||||
return form({'id_switch': switch_id, 'topoform': port_form}, 'topologie/switch.html', request)
|
||||
|
||||
|
||||
|
@ -450,37 +482,34 @@ def edit_switch(request, switch, switch_id):
|
|||
""" Edition d'un switch. Permet de chambre nombre de ports,
|
||||
place dans le stack, interface et machine associée"""
|
||||
|
||||
switch_form = EditSwitchForm(request.POST or None, instance=switch)
|
||||
machine_form = EditMachineForm(
|
||||
switch_form = EditSwitchForm(
|
||||
request.POST or None,
|
||||
instance=switch.switch_interface.machine,
|
||||
instance=switch,
|
||||
user=request.user
|
||||
)
|
||||
interface_form = EditInterfaceForm(
|
||||
request.POST or None,
|
||||
instance=switch.switch_interface,
|
||||
instance=switch.interface_set.first(),
|
||||
user=request.user
|
||||
)
|
||||
domain_form = DomainForm(
|
||||
request.POST or None,
|
||||
instance=switch.switch_interface.domain
|
||||
instance=switch.interface_set.first().domain
|
||||
)
|
||||
if switch_form.is_valid() and machine_form.is_valid()\
|
||||
and interface_form.is_valid():
|
||||
new_interface = interface_form.save(commit=False)
|
||||
new_machine = machine_form.save(commit=False)
|
||||
new_switch_instance = switch_form.save(commit=False)
|
||||
if switch_form.is_valid() and interface_form.is_valid():
|
||||
new_switch = switch_form.save(commit=False)
|
||||
new_interface_instance = interface_form.save(commit=False)
|
||||
new_domain = domain_form.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_machine.save()
|
||||
new_switch.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in machine_form.changed_data
|
||||
field for field in switch_form.changed_data
|
||||
)
|
||||
)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
new_interface_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in interface_form.changed_data)
|
||||
|
@ -491,25 +520,134 @@ def edit_switch(request, switch, switch_id):
|
|||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in domain_form.changed_data)
|
||||
)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_switch_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in switch_form.changed_data)
|
||||
)
|
||||
messages.success(request, "Le switch a bien été modifié")
|
||||
return redirect(reverse('topologie:index'))
|
||||
i_mbf_param = generate_ipv4_mbf_param( interface_form, False )
|
||||
i_mbf_param = generate_ipv4_mbf_param(interface_form, False )
|
||||
return form({
|
||||
'id_switch': switch_id,
|
||||
'topoform': switch_form,
|
||||
'machineform': machine_form,
|
||||
'interfaceform': interface_form,
|
||||
'topoform': interface_form,
|
||||
'machineform': switch_form,
|
||||
'domainform': domain_form,
|
||||
'i_mbf_param': i_mbf_param
|
||||
}, 'topologie/switch.html', request)
|
||||
'i_mbf_param': i_mbf_param,
|
||||
'device' : 'switch',
|
||||
}, 'topologie/topo_more.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_create(AccessPoint)
|
||||
def new_ap(request):
|
||||
""" Creation d'une ap. Cree en meme temps l'interface et la machine
|
||||
associée. Vue complexe. Appelle successivement les 3 models forms
|
||||
adaptés : machine, interface, domain et switch"""
|
||||
ap = AddAccessPointForm(
|
||||
request.POST or None,
|
||||
user=request.user
|
||||
)
|
||||
interface = AddInterfaceForm(
|
||||
request.POST or None,
|
||||
user=request.user
|
||||
)
|
||||
domain = DomainForm(
|
||||
request.POST or None,
|
||||
)
|
||||
if ap.is_valid() and interface.is_valid():
|
||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||
if not user:
|
||||
messages.error(request, "L'user association n'existe pas encore,\
|
||||
veuillez le créer ou le linker dans preferences")
|
||||
return redirect(reverse('topologie:index'))
|
||||
new_ap = ap.save(commit=False)
|
||||
new_ap.user = user
|
||||
new_interface = interface.save(commit=False)
|
||||
domain.instance.interface_parent = new_interface
|
||||
if domain.is_valid():
|
||||
new_domain_instance = domain.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_ap.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_interface.machine = new_ap
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
new_domain_instance.interface_parent = new_interface
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_domain_instance.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "La borne a été créé")
|
||||
return redirect(reverse('topologie:index-ap'))
|
||||
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
||||
return form({
|
||||
'topoform': interface,
|
||||
'machineform': ap,
|
||||
'domainform': domain,
|
||||
'i_mbf_param': i_mbf_param,
|
||||
'device' : 'wifi ap',
|
||||
}, 'topologie/topo_more.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_edit(AccessPoint)
|
||||
def edit_ap(request, ap, ap_id):
|
||||
""" Edition d'un switch. Permet de chambre nombre de ports,
|
||||
place dans le stack, interface et machine associée"""
|
||||
interface_form = EditInterfaceForm(
|
||||
request.POST or None,
|
||||
user=request.user,
|
||||
instance=ap.interface_set.first()
|
||||
)
|
||||
ap_form = EditAccessPointForm(
|
||||
request.POST or None,
|
||||
user=request.user,
|
||||
instance=ap
|
||||
)
|
||||
domain_form = DomainForm(
|
||||
request.POST or None,
|
||||
instance=ap.interface_set.first().domain
|
||||
)
|
||||
if ap_form.is_valid() and interface_form.is_valid():
|
||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||
if not user:
|
||||
messages.error(request, "L'user association n'existe pas encore,\
|
||||
veuillez le créer ou le linker dans preferences")
|
||||
return redirect(reverse('topologie:index-ap'))
|
||||
new_ap = ap_form.save(commit=False)
|
||||
new_interface = interface_form.save(commit=False)
|
||||
new_domain = domain_form.save(commit=False)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_ap.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment(
|
||||
"Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in ap_form.changed_data)
|
||||
)
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_interface.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in interface_form.changed_data)
|
||||
)
|
||||
reversion.set_comment("Création")
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
new_domain.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in domain_form.changed_data)
|
||||
)
|
||||
messages.success(request, "La borne a été modifiée")
|
||||
return redirect(reverse('topologie:index-ap'))
|
||||
i_mbf_param = generate_ipv4_mbf_param(interface_form, False )
|
||||
return form({
|
||||
'topoform': interface_form,
|
||||
'machineform': ap_form,
|
||||
'domainform': domain_form,
|
||||
'i_mbf_param': i_mbf_param,
|
||||
'device' : 'wifi ap',
|
||||
}, 'topologie/topo_more.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_create(Room)
|
||||
def new_room(request):
|
||||
|
|
|
@ -41,8 +41,17 @@ from django.utils import timezone
|
|||
from django.contrib.auth.models import Group, Permission
|
||||
|
||||
from preferences.models import OptionalUser
|
||||
from .models import User, ServiceUser, School, ListRight, Whitelist
|
||||
from .models import Ban, Adherent, Club
|
||||
from .models import (
|
||||
User,
|
||||
ServiceUser,
|
||||
School,
|
||||
ListRight,
|
||||
Whitelist,
|
||||
ListShell,
|
||||
Ban,
|
||||
Adherent,
|
||||
Club
|
||||
)
|
||||
from re2o.utils import remove_user_room
|
||||
|
||||
from re2o.field_permissions import FieldPermissionFormMixin
|
||||
|
@ -460,6 +469,18 @@ class SchoolForm(ModelForm):
|
|||
self.fields['name'].label = 'Établissement'
|
||||
|
||||
|
||||
class ShellForm(ModelForm):
|
||||
"""Edition, creation d'un école"""
|
||||
class Meta:
|
||||
model = ListShell
|
||||
fields = ['shell']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(ShellForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['shell'].label = 'Nom du shell'
|
||||
|
||||
|
||||
class ListRightForm(ModelForm):
|
||||
"""Edition, d'un groupe , équivalent à un droit
|
||||
Ne peremet pas d'editer le gid, car il sert de primary key"""
|
||||
|
|
|
@ -54,7 +54,11 @@ class Command(BaseCommand):
|
|||
raise CommandError(msg)
|
||||
|
||||
shells = ListShell.objects.all()
|
||||
self.stdout.write("Choisissez un shell pour l'utilisateur %s :" % target_user.pseudo)
|
||||
|
||||
current_shell = "inconnu"
|
||||
if target_user.shell:
|
||||
current_shell = target_user.shell.get_pretty_name()
|
||||
self.stdout.write("Choisissez un shell pour l'utilisateur %s (le shell actuel est %s) :" % (target_user.pseudo, current_shell))
|
||||
for shell in shells:
|
||||
self.stdout.write("%d - %s (%s)" % (shell.id, shell.get_pretty_name(), shell.shell))
|
||||
shell_id = input("Entrez un nombre : ")
|
||||
|
|
80
users/management/commands/derniere_connexion.py
Normal file
80
users/management/commands/derniere_connexion.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# ⁻*- mode: python; coding: utf-8 -*-
|
||||
# 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 © 2018 Benjamin Graillot
|
||||
#
|
||||
# Copyright © 2013-2015 Raphaël-David Lasseri <lasseri@crans.org>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.timezone import make_aware
|
||||
|
||||
from users.models import User
|
||||
|
||||
# Une liste d'expressions régulières à chercher dans les logs.
|
||||
# Elles doivent contenir un groupe 'date' et un groupe 'user'.
|
||||
# Pour le CAS on prend comme entrée cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /'
|
||||
COMPILED_REGEX = map(re.compile, [
|
||||
r'^(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:'r'dovecot.*Login: user=<|'r'sshd.*Accepted.*for 'r')(?P<user>[^ >]+).*$',
|
||||
r'^(?P<date>.*) LOGIN INFO User logged in : (?P<user>.*)',
|
||||
r'WHO: \[username: (?P<user>.*)\] WHEN: (?P<date>.* CET .*)',
|
||||
r'WHO: \[username: (?P<user>.*)\] WHEN: (?P<date>.* CEST .*)'
|
||||
])
|
||||
|
||||
# Les formats de date en strftime associés aux expressions ci-dessus.
|
||||
DATE_FORMATS = [
|
||||
"%Y-%m-%dT%H:%M:%S",
|
||||
"%d/%b/%Y:%H:%M:%S",
|
||||
"%a %b %d CET %H:%M:%S%Y",
|
||||
"%a %b %d CEST %H:%M:%S%Y"
|
||||
]
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Update the time of the latest connection for users by matching stdin against a set of regular expressions'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
def parse_logs(logfile):
|
||||
"""
|
||||
Parse les logs sur l'entrée standard et rempli un dictionnaire
|
||||
ayant pour clef le pseudo de l'adherent
|
||||
"""
|
||||
global COMPILED_REGEX, DATE_FORMATS
|
||||
|
||||
parsed_log = {}
|
||||
for line in logfile:
|
||||
for i, regex in enumerate(COMPILED_REGEX):
|
||||
m = regex.match(line)
|
||||
if m:
|
||||
parsed_log[m.group('user')] = make_aware(datetime.strptime(m.group('date'), DATE_FORMATS[i]))
|
||||
return parsed_log
|
||||
|
||||
parsed_log = parse_logs(sys.stdin)
|
||||
|
||||
for pseudo in parsed_log:
|
||||
for user in User.objects.filter(pseudo=pseudo):
|
||||
last_login = parsed_log.get(user.pseudo, user.last_login)
|
||||
if not user.last_login:
|
||||
user.last_login = last_login
|
||||
elif last_login > user.last_login:
|
||||
user.last_login = last_login
|
||||
user.save()
|
19
users/migrations/0070_auto_20180324_1906.py
Normal file
19
users/migrations/0070_auto_20180324_1906.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-03-24 18:06
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0069_club_mailing'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='listshell',
|
||||
options={'permissions': (('view_listshell', "Peut voir un objet shell quelqu'il soit"),)},
|
||||
),
|
||||
]
|
|
@ -300,6 +300,12 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin):
|
|||
""" Renvoie seulement le nom"""
|
||||
return self.surname
|
||||
|
||||
@property
|
||||
def get_shell(self):
|
||||
""" A utiliser de préférence, prend le shell par défaut
|
||||
si il n'est pas défini"""
|
||||
return self.shell or OptionalUser.get_cached_value('shell_default')
|
||||
|
||||
def end_adhesion(self):
|
||||
""" Renvoie la date de fin d'adhésion d'un user. Examine les objets
|
||||
cotisation"""
|
||||
|
@ -502,8 +508,8 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin):
|
|||
user_ldap.gid = LDAP['user_gid']
|
||||
user_ldap.user_password = self.password[:6] + self.password[7:]
|
||||
user_ldap.sambat_nt_password = self.pwd_ntlm.upper()
|
||||
if self.shell:
|
||||
user_ldap.login_shell = self.shell.shell
|
||||
if self.get_shell:
|
||||
user_ldap.login_shell = str(self.get_shell)
|
||||
if self.state == self.STATE_DISABLED:
|
||||
user_ldap.shadowexpire = str(0)
|
||||
else:
|
||||
|
@ -1248,10 +1254,67 @@ class ListShell(models.Model):
|
|||
|
||||
shell = models.CharField(max_length=255, unique=True)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_listshell", "Peut voir un objet shell quelqu'il soit"),
|
||||
)
|
||||
|
||||
def get_instance(shellid, *args, **kwargs):
|
||||
return ListShell.objects.get(pk=shellid)
|
||||
|
||||
def get_pretty_name(self):
|
||||
"""Return the canonical name of the shell"""
|
||||
return self.shell.split("/")[-1]
|
||||
|
||||
def can_create(user_request, *args, **kwargs):
|
||||
"""Check if an user can create a ListShell object.
|
||||
|
||||
:param user_request: The user who wants to create a user object.
|
||||
:return: a message and a boolean which is True if the user can create.
|
||||
"""
|
||||
return user_request.has_perm('users.add_listshell'), u"Vous n'avez pas le\
|
||||
droit de créer des shells"
|
||||
|
||||
def can_edit(self, user_request, *args, **kwargs):
|
||||
"""Check if an user can edit a ListShell object.
|
||||
|
||||
:param self: The Shell which is to be edited.
|
||||
:param user_request: The user who requests to edit self.
|
||||
:return: a message and a boolean which is True if edition is granted.
|
||||
"""
|
||||
return user_request.has_perm('users.change_listshell'), u"Vous n'avez pas le\
|
||||
droit d'éditer des shells"
|
||||
|
||||
def can_delete(self, user_request, *args, **kwargs):
|
||||
"""Check if an user can delete a ListShell object.
|
||||
|
||||
:param self: The Shell which is to be deleted.
|
||||
:param user_request: The user who requests deletion.
|
||||
:return: True if deletion is granted, and a message.
|
||||
"""
|
||||
return user_request.has_perm('users.delete_listshell'), u"Vous n'avez pas le\
|
||||
droit de supprimer des shells"
|
||||
|
||||
def can_view_all(user_request, *args, **kwargs):
|
||||
"""Check if an user can access to the list of every ListShell objects
|
||||
|
||||
:param user_request: The user who wants to view the list.
|
||||
:return: True if the user can view the list and an explanation message.
|
||||
"""
|
||||
return user_request.has_perm('users.view_listshell'), u"Vous n'avez pas le\
|
||||
droit de voir les shells"
|
||||
|
||||
def can_view(self, user_request, *args, **kwargs):
|
||||
"""Check if an user can view a ListShell object.
|
||||
|
||||
:param self: The targeted ListShell instance.
|
||||
:param user_request: The user who ask for viewing the target.
|
||||
:return: A boolean telling if the acces is granted and an explanation
|
||||
text
|
||||
"""
|
||||
return user_request.has_perm('users.view_listshell'), u"Vous n'avez pas le\
|
||||
droit de voir les shells"
|
||||
|
||||
def __str__(self):
|
||||
return self.shell
|
||||
|
||||
|
|
47
users/templates/users/aff_shell.html
Normal file
47
users/templates/users/aff_shell.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
{% load acl %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shell</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for shell in shell_list %}
|
||||
<tr>
|
||||
<td>{{ shell.shell }}</td>
|
||||
<td class="text-right">
|
||||
{% can_delete shell %}
|
||||
{% include 'buttons/suppr.html' with href='users:del-shell' id=shell.id %}
|
||||
{% acl_end %}
|
||||
{% can_edit shell %}
|
||||
{% include 'buttons/edit.html' with href='users:edit-shell' id=shell.id %}
|
||||
{% acl_end %}
|
||||
{% include 'buttons/history.html' with href='users:history' name='shell' id=shell.id %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
@ -21,7 +21,7 @@ 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.
|
||||
{% endcomment %}
|
||||
|
||||
<div class="table-responsive">
|
||||
{% if users_list.paginator %}
|
||||
{% include "pagination.html" with list=users_list %}
|
||||
{% endif %}
|
||||
|
@ -60,3 +60,4 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% if users_list.paginator %}
|
||||
{% include "pagination.html" with list=users_list %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
41
users/templates/users/index_shell.html
Normal file
41
users/templates/users/index_shell.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "users/sidebar.html" %}
|
||||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load acl %}
|
||||
|
||||
{% block title %}Utilisateurs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Liste des Shells</h2>
|
||||
{% can_create ListShell %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-shell' %}"><i class="fa fa-plus"></i> Ajouter un shell</a>
|
||||
{% acl_end %}
|
||||
{% include "users/aff_shell.html" with shell_list=shell_list %}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{% endblock %}
|
||||
|
|
@ -69,6 +69,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
Établissements
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all ListShell %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "users:index-shell" %}">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
Liste des shells
|
||||
</a>
|
||||
{% acl_end %}
|
||||
{% can_view_all ListRight %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "users:index-listright" %}">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
|
|
|
@ -80,10 +80,22 @@ urlpatterns = [
|
|||
name='edit-listright'
|
||||
),
|
||||
url(r'^del_listright/$', views.del_listright, name='del-listright'),
|
||||
url(r'^add_shell/$', views.add_shell, name='add-shell'),
|
||||
url(
|
||||
r'^edit_shell/(?P<shellid>[0-9]+)$',
|
||||
views.edit_shell,
|
||||
name='edit-shell'
|
||||
),
|
||||
url(
|
||||
r'^del_shell/(?P<shellid>[0-9]+)$',
|
||||
views.del_shell,
|
||||
name='del-shell'
|
||||
),
|
||||
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
||||
url(r'^index_ban/$', views.index_ban, name='index-ban'),
|
||||
url(r'^index_white/$', views.index_white, name='index-white'),
|
||||
url(r'^index_school/$', views.index_school, name='index-school'),
|
||||
url(r'^index_shell/$', views.index_shell, name='index-shell'),
|
||||
url(r'^index_listright/$', views.index_listright, name='index-listright'),
|
||||
url(
|
||||
r'^index_serviceusers/$',
|
||||
|
|
|
@ -45,8 +45,10 @@ from django.db import IntegrityError
|
|||
from django.utils import timezone
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
|
||||
|
||||
|
@ -63,6 +65,7 @@ from users.models import (
|
|||
ServiceUser,
|
||||
Adherent,
|
||||
Club,
|
||||
ListShell,
|
||||
)
|
||||
from users.forms import (
|
||||
BanForm,
|
||||
|
@ -72,6 +75,7 @@ from users.forms import (
|
|||
NewListRightForm,
|
||||
StateForm,
|
||||
SchoolForm,
|
||||
ShellForm,
|
||||
EditServiceUserForm,
|
||||
ServiceUserForm,
|
||||
ListRightForm,
|
||||
|
@ -273,8 +277,10 @@ def del_group(request, user, userid, listrightid):
|
|||
with transaction.atomic(), reversion.create_revision():
|
||||
user.groups.remove(ListRight.objects.get(id=listrightid))
|
||||
user.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Suppression de droit")
|
||||
messages.success(request, "Droit supprimé à %s" % user)
|
||||
return redirect(reverse('users:index-listright'))
|
||||
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -492,6 +498,55 @@ def del_school(request, instances):
|
|||
return form({'userform': school, 'action_name': 'Supprimer'}, 'users/user.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_create(ListShell)
|
||||
def add_shell(request):
|
||||
""" Ajouter un shell à la base de donnée"""
|
||||
shell = ShellForm(request.POST or None)
|
||||
if shell.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
shell.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "Le shell a été ajouté")
|
||||
return redirect(reverse('users:index-shell'))
|
||||
return form({'userform': shell, 'action_name':'Ajouter'}, 'users/user.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_edit(ListShell)
|
||||
def edit_shell(request, shell_instance, shellid):
|
||||
""" Editer un shell à partir du shellid"""
|
||||
shell = ShellForm(request.POST or None, instance=shell_instance)
|
||||
if shell.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
shell.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in shell.changed_data
|
||||
))
|
||||
messages.success(request, "Le shell a été modifié")
|
||||
return redirect(reverse('users:index-shell'))
|
||||
return form({'userform': shell, 'action_name':'Editer'}, 'users/user.html', request)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_delete(ListShell)
|
||||
def del_shell(request, shell, shellid):
|
||||
"""Destruction d'un shell"""
|
||||
if request.method == "POST":
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
shell.delete()
|
||||
reversion.set_user(request.user)
|
||||
messages.success(request, "Le shell a été détruit")
|
||||
return redirect(reverse('users:index-shell'))
|
||||
return form(
|
||||
{'objet': shell, 'objet_name': 'shell'},
|
||||
'users/delete.html',
|
||||
request
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_create(ListRight)
|
||||
def add_listright(request):
|
||||
|
@ -690,7 +745,7 @@ def index_white(request):
|
|||
@login_required
|
||||
@can_view_all(School)
|
||||
def index_school(request):
|
||||
""" Affiche l'ensemble des établissement, need droit cableur """
|
||||
""" Affiche l'ensemble des établissement"""
|
||||
school_list = School.objects.order_by('name')
|
||||
return render(
|
||||
request,
|
||||
|
@ -699,10 +754,22 @@ def index_school(request):
|
|||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_view_all(ListShell)
|
||||
def index_shell(request):
|
||||
""" Affiche l'ensemble des shells"""
|
||||
shell_list = ListShell.objects.order_by('shell')
|
||||
return render(
|
||||
request,
|
||||
'users/index_shell.html',
|
||||
{'shell_list': shell_list}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@can_view_all(ListRight)
|
||||
def index_listright(request):
|
||||
""" Affiche l'ensemble des droits , need droit cableur """
|
||||
""" Affiche l'ensemble des droits"""
|
||||
listright_list = ListRight.objects.order_by('unix_name')\
|
||||
.prefetch_related('permissions').prefetch_related('user_set')
|
||||
return render(
|
||||
|
|
Loading…
Reference in a new issue