mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-26 14:42:25 +00:00
Merge branch 'master' into ouverture_des_ports
This commit is contained in:
commit
4674c55d68
14 changed files with 232 additions and 50 deletions
136
README.md
136
README.md
|
@ -4,17 +4,28 @@ Gnu public license v2.0
|
||||||
|
|
||||||
## Avant propos
|
## Avant propos
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Il utilise le framework django avec python3. Il permet de gérer les adhérents, les machines, les factures, les droits d'accès, les switchs et la topologie du réseau.
|
Il utilise le framework django avec python3. Il permet de gérer les adhérents,
|
||||||
De cette manière, il est possible de pluguer très facilement des services dessus, qui accèdent à la base de donnée en passant par django (ex : dhcp), en chargeant la liste de toutes les mac-ip, ou la liste des mac-ip autorisées sur le réseau (adhérent à jour de cotisation).
|
les machines, les factures, les droits d'accès, les switchs et la topologie du
|
||||||
|
réseau.
|
||||||
|
De cette manière, il est possible de pluguer très facilement des services
|
||||||
|
dessus, qui accèdent à la base de donnée en passant par django (ex : dhcp), en
|
||||||
|
chargeant la liste de toutes les mac-ip, ou la liste des mac-ip autorisées sur
|
||||||
|
le réseau (adhérent à jour de cotisation).
|
||||||
|
|
||||||
#Installation
|
#Installation
|
||||||
|
|
||||||
## Installation des dépendances
|
## Installation des dépendances
|
||||||
|
|
||||||
L'installation comporte 3 partie : le serveur web où se trouve le depot re2o ainsi que toutes ses dépendances, le serveur bdd (mysql ou pgsql) et le serveur ldap. Ces 3 serveurs peuvent en réalité être la même machine, ou séparés (recommandé en production).
|
L'installation comporte 3 partie : le serveur web où se trouve le depot re2o
|
||||||
Le serveur web sera nommé serveur A, le serveur bdd serveur B et le serveur ldap serveur C.
|
ainsi que toutes ses dépendances, le serveur bdd (mysql ou pgsql) et le
|
||||||
|
serveur ldap. Ces 3 serveurs peuvent en réalité être la même machine, ou séparés
|
||||||
|
(recommandé en production).
|
||||||
|
Le serveur web sera nommé serveur A, le serveur bdd serveur B et le serveur ldap
|
||||||
|
serveur C.
|
||||||
|
|
||||||
### Prérequis sur le serveur A
|
### Prérequis sur le serveur A
|
||||||
|
|
||||||
|
@ -66,49 +77,88 @@ Sur le serveur C (ldap), avec apt :
|
||||||
### Installation sur le serveur principal A
|
### Installation sur le serveur principal A
|
||||||
|
|
||||||
Cloner le dépot re2o à partir du gitlab, par exemple dans /var/www/re2o.
|
Cloner le dépot re2o à partir du gitlab, par exemple dans /var/www/re2o.
|
||||||
Ensuite, il faut créer le fichier settings_local.py dans le sous dossier re2o, un settings_local.example.py est présent. Les options sont commentées, et des options par défaut existent.
|
Ensuite, il faut créer le fichier settings_local.py dans le sous dossier re2o,
|
||||||
|
un settings_local.example.py est présent. Les options sont commentées, et des
|
||||||
|
options par défaut existent.
|
||||||
|
|
||||||
En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans settings_local.py
|
En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et
|
||||||
|
un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans
|
||||||
|
settings_local.py
|
||||||
|
|
||||||
### Installation du serveur mysql/postgresql sur B
|
### Installation du serveur mysql/postgresql sur B
|
||||||
|
|
||||||
Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de donnée re2o, ainsi qu'un user re2o et un mot de passe associé. Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl nécessaire pour que A puisse l'utiliser.
|
Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de
|
||||||
|
donnée re2o, ainsi qu'un user re2o et un mot de passe associé.
|
||||||
|
Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl
|
||||||
|
nécessaire pour que A puisse l'utiliser.
|
||||||
|
|
||||||
|
#### Mysql
|
||||||
Voici les étapes à éxecuter pour mysql :
|
Voici les étapes à éxecuter pour mysql :
|
||||||
* CREATE DATABASE re2o collate='utf8_general_ci';
|
* CREATE DATABASE re2o collate='utf8_general_ci';
|
||||||
* CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
|
* CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
|
||||||
* GRANT ALL PRIVILEGES ON re2o.* TO 'newuser'@'localhost';
|
* GRANT ALL PRIVILEGES ON re2o.* TO 'newuser'@'localhost';
|
||||||
* FLUSH PRIVILEGES;
|
* FLUSH PRIVILEGES;
|
||||||
|
|
||||||
Si les serveurs A et B ne sont pas la même machine, il est nécessaire de remplacer localhost par l'ip avec laquelle A contacte B dans les commandes du dessus.
|
#### Postgresql
|
||||||
Une fois ces commandes effectuées, ne pas oublier de vérifier que newuser et password sont présents dans settings_local.py
|
* CREATE DATABASE re2o ENCODING 'UTF8' LC_COLLATE='fr_FR.UTF-8'
|
||||||
|
LC_CTYPE='fr_FR.UTF-8';
|
||||||
|
* CREATE USER newuser with password 'password';
|
||||||
|
* ALTER DATABASE re2o owner to newuser;
|
||||||
|
|
||||||
|
Si les serveurs A et B ne sont pas la même machine, il est nécessaire de
|
||||||
|
remplacer localhost par l'ip avec laquelle A contacte B dans les commandes
|
||||||
|
du dessus.
|
||||||
|
Une fois ces commandes effectuées, ne pas oublier de vérifier que newuser et
|
||||||
|
password sont présents dans settings_local.py
|
||||||
|
|
||||||
### Installation du serveur ldap sur le serveur C
|
### Installation du serveur ldap sur le serveur C
|
||||||
|
|
||||||
Ceci se fait en plusieurs étapes :
|
Ceci se fait en plusieurs étapes :
|
||||||
* générer un login/mdp administrateur (par example mkpasswd sous debian)
|
* générer un login/mdp administrateur (par example mkpasswd sous debian)
|
||||||
* Copier depuis re2o/install_utils (dans le dépot re2o) les fichiers db.ldiff et schema.ldiff (normalement sur le serveur A) sur le serveur C (par ex dans /tmp)
|
* Copier depuis re2o/install_utils (dans le dépot re2o) les fichiers db.ldiff
|
||||||
* Hasher le mot de passe généré en utilisant la commande slappasswd (installée par slapd)
|
et schema.ldiff (normalement sur le serveur A) sur le serveur C
|
||||||
* Remplacer toutes les sections FILL_IN par le hash dans schema.ldiff et db.ldiff
|
(par ex dans /tmp)
|
||||||
* Remplacer dans schema.ldiff et db.ldiff 'dc=example,dc=org' par le suffixe de l'organisation
|
* Hasher le mot de passe généré en utilisant la commande slappasswd
|
||||||
|
(installée par slapd)
|
||||||
|
* Remplacer toutes les sections FILL_IN par le hash dans schema.ldiff et
|
||||||
|
db.ldiff
|
||||||
|
* Remplacer dans schema.ldiff et db.ldiff 'dc=example,dc=org' par le
|
||||||
|
suffixe de l'organisation
|
||||||
* Arréter slapd
|
* Arréter slapd
|
||||||
* Supprimer les données existantes : '''rm -rf /etc/ldap/slapd.d/*''' et '''rm -rf /var/lib/ldap/*'''
|
* Supprimer les données existantes : '''rm -rf /etc/ldap/slapd.d/*''' et
|
||||||
* Injecter le nouveau schéma : '''slapadd -n 0 -l schema.ldiff -F /etc/ldap/slapd.d/''' et '''slapadd -n 1 -l db.ldiff'''
|
'''rm -rf /var/lib/ldap/*'''
|
||||||
* Réparer les permissions (chown -R openldap:openldap /etc/ldap/slapd.d et chown -R openldap:openldap /var/lib/ldap) puis relancer slapd
|
* Injecter le nouveau schéma :
|
||||||
|
'''slapadd -n 0 -l schema.ldiff -F /etc/ldap/slapd.d/''' et
|
||||||
|
'''slapadd -n 1 -l db.ldiff'''
|
||||||
|
* Réparer les permissions (chown -R openldap:openldap /etc/ldap/slapd.d et
|
||||||
|
chown -R openldap:openldap /var/lib/ldap) puis relancer slapd
|
||||||
|
|
||||||
Normalement le serveur ldap démare et est fonctionnel. Par défaut tls n'est pas activé, il faut pour cela modifier le schéma pour indiquer l'emplacement du certificat.
|
Pour visualiser et éditer le ldap, l'utilisation de shelldap est fortement
|
||||||
Pour visualiser et éditer le ldap, l'utilisation de shelldap est fortement recommandée, en utilisant en binddn cn=admin,dc=ldap,dc=example,dc=org et binddpw le mot de passe admin.
|
recommandée, en utilisant en binddn et basedn tous deux égaux à 'cn=config' et
|
||||||
|
binddpw le mot de passe admin.
|
||||||
|
|
||||||
|
Rajouter (exemple de chemin de fichier avec un certif LE):
|
||||||
|
`olcTLSCertificateKeyFile: /etc/letsencrypt/live/HOSTNAME/privkey.pem
|
||||||
|
olcTLSCACertificateFile: /etc/letsencrypt/live/HOSTNAME/chain.pem
|
||||||
|
olcTLSCertificateFile: /etc/letsencrypt/live/HOSTNAME/cert.pem `
|
||||||
|
|
||||||
|
Mettre à jour la partie ldap du `settings_local.py` (mettre 'TLS' à True
|
||||||
|
si besoin, user cn=config,dc=example,dc=org et mot de passe
|
||||||
|
ldap choisi précédemment).
|
||||||
|
|
||||||
## Configuration initiale
|
## Configuration initiale
|
||||||
|
|
||||||
Normalement à cette étape, le ldap et la bdd sql sont configurées correctement.
|
Normalement à cette étape, le ldap et la bdd sql sont configurées correctement.
|
||||||
|
|
||||||
Il faut alors lancer dans le dépot re2o '''python3 manage.py migrate''' qui va structurer initialement la base de données.
|
Il faut alors lancer dans le dépot re2o '''python3 manage.py migrate''' qui
|
||||||
Les migrations sont normalement comitées au fur et à mesure, néanmoins cette étape peut crasher, merci de reporter les bugs.
|
va structurer initialement la base de données.
|
||||||
|
Les migrations sont normalement comitées au fur et à mesure, néanmoins cette
|
||||||
|
étape peut crasher, merci de reporter les bugs.
|
||||||
|
|
||||||
## Démarer le site web
|
## Démarer le site web
|
||||||
|
|
||||||
Il faut utiliser un moteur pour servir le site web. Nginx ou apache2 sont recommandés.
|
Il faut utiliser un moteur pour servir le site web. Nginx ou apache2 sont
|
||||||
|
recommandés.
|
||||||
Pour apache2 :
|
Pour apache2 :
|
||||||
* apt install apache2
|
* apt install apache2
|
||||||
* apt install libapache2-mod-wsgi-py3 (pour le module wsgi)
|
* apt install libapache2-mod-wsgi-py3 (pour le module wsgi)
|
||||||
|
@ -119,9 +169,12 @@ re2o/wsgi.py permet de fonctionner avec apache2 en production
|
||||||
## Configuration avancée
|
## Configuration avancée
|
||||||
|
|
||||||
Une fois démaré, le site web devrait être accessible.
|
Une fois démaré, le site web devrait être accessible.
|
||||||
Pour créer un premier user, faire '''python3 manage.py createsuperuser''' qui va alors créer un user admin.
|
Pour créer un premier user, faire '''python3 manage.py createsuperuser'''
|
||||||
Il est conseillé de créer alors les droits cableur, bureau, trésorier et infra, qui n'existent pas par défaut dans le menu adhérents.
|
qui va alors créer un user admin.
|
||||||
Il est également conseillé de créer un user portant le nom de l'association/organisation, qui possedera l'ensemble des machines.
|
Il est conseillé de créer alors les droits cableur, bureau, trésorier et infra,
|
||||||
|
qui n'existent pas par défaut dans le menu adhérents.
|
||||||
|
Il est également conseillé de créer un user portant le nom de
|
||||||
|
l'association/organisation, qui possedera l'ensemble des machines.
|
||||||
|
|
||||||
## Installations Optionnelles
|
## Installations Optionnelles
|
||||||
### Générer le schéma des dépendances
|
### Générer le schéma des dépendances
|
||||||
|
@ -134,24 +187,37 @@ Pour cela :
|
||||||
|
|
||||||
## Fonctionnement général
|
## Fonctionnement général
|
||||||
|
|
||||||
Re2o est séparé entre les models, qui sont visible sur le schéma des dépendances. Il s'agit en réalité des tables sql, et les fields etant les colonnes.
|
Re2o est séparé entre les models, qui sont visible sur le schéma des
|
||||||
Ceci dit il n'est jamais nécessaire de toucher directement au sql, django procédant automatiquement à tout cela.
|
dépendances. Il s'agit en réalité des tables sql, et les fields etant les
|
||||||
On crée donc différents models (user, right pour les droits des users, interfaces, IpList pour l'ensemble des adresses ip, etc)
|
colonnes.
|
||||||
|
Ceci dit il n'est jamais nécessaire de toucher directement au sql, django
|
||||||
|
procédant automatiquement à tout cela.
|
||||||
|
On crée donc différents models (user, right pour les droits des users,
|
||||||
|
interfaces, IpList pour l'ensemble des adresses ip, etc)
|
||||||
|
|
||||||
Du coté des forms, il s'agit des formulaire d'édition des models. Il s'agit de ModelForms django, qui héritent des models très simplement, voir la documentation django models forms.
|
Du coté des forms, il s'agit des formulaire d'édition des models. Il
|
||||||
|
s'agit de ModelForms django, qui héritent des models très simplement, voir la
|
||||||
|
documentation django models forms.
|
||||||
|
|
||||||
Enfin les views, générent les pages web à partir des forms et des templates.
|
Enfin les views, générent les pages web à partir des forms et des templates.
|
||||||
|
|
||||||
## Fonctionnement avec les services
|
## Fonctionnement avec les services
|
||||||
|
|
||||||
Les services dhcp.py, dns.py etc accèdent aux données via des vues rest.
|
Les services dhcp.py, dns.py etc accèdent aux données via des vues rest.
|
||||||
Celles-ci se trouvent dans machines/views.py. Elles sont générées via machines/serializers.py qui génère les vues. IL s'agit de vues en json utilisées par re2o-tools pour récupérer les données.
|
Celles-ci se trouvent dans machines/views.py. Elles sont générées via
|
||||||
Il est nécessaire de créer un user dans re2o avec le droit serveur qui permet d'accéder à ces vues, utilisé par re2o-tools.
|
machines/serializers.py qui génère les vues. IL s'agit de vues en json utilisées
|
||||||
|
par re2o-tools pour récupérer les données.
|
||||||
|
Il est nécessaire de créer un user dans re2o avec le droit serveur qui permet
|
||||||
|
d'accéder à ces vues, utilisé par re2o-tools.
|
||||||
|
|
||||||
# Requète en base de donnée
|
# Requète en base de donnée
|
||||||
|
|
||||||
Pour avoir un shell, il suffit de lancer '''python3 manage.py shell'''
|
Pour avoir un shell, il suffit de lancer '''python3 manage.py shell'''
|
||||||
Pour charger des objets, example avec User, faire : ''' from users.models import User'''
|
Pour charger des objets, example avec User, faire :
|
||||||
Pour charger les objets django, il suffit de faire User.objects.all() pour tous les users par exemple.
|
''' from users.models import User'''
|
||||||
Il est ensuite aisé de faire des requètes, par exemple User.objects.filter(pseudo='test')
|
Pour charger les objets django, il suffit de faire User.objects.all()
|
||||||
Des exemples et la documentation complète sur les requètes django sont disponible sur le site officiel.
|
pour tous les users par exemple.
|
||||||
|
Il est ensuite aisé de faire des requètes, par exemple
|
||||||
|
User.objects.filter(pseudo='test')
|
||||||
|
Des exemples et la documentation complète sur les requètes django sont
|
||||||
|
disponible sur le site officiel.
|
||||||
|
|
|
@ -60,6 +60,9 @@ class EditInterfaceForm(ModelForm):
|
||||||
self.fields['mac_address'].label = 'Adresse mac'
|
self.fields['mac_address'].label = 'Adresse mac'
|
||||||
self.fields['type'].label = 'Type de machine'
|
self.fields['type'].label = 'Type de machine'
|
||||||
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
||||||
|
if "ipv4" in self.fields:
|
||||||
|
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||||
|
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
|
||||||
if "machine" in self.fields:
|
if "machine" in self.fields:
|
||||||
self.fields['machine'].queryset = Machine.objects.all().select_related('user')
|
self.fields['machine'].queryset = Machine.objects.all().select_related('user')
|
||||||
|
|
||||||
|
@ -110,11 +113,11 @@ class DomainForm(AliasForm):
|
||||||
fields = ['name']
|
fields = ['name']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if 'name_user' in kwargs:
|
if 'user' in kwargs:
|
||||||
name_user = kwargs.pop('name_user')
|
user = kwargs.pop('user')
|
||||||
nb_machine = kwargs.pop('nb_machine')
|
nb_machine = kwargs.pop('nb_machine')
|
||||||
initial = kwargs.get('initial', {})
|
initial = kwargs.get('initial', {})
|
||||||
initial['name'] = name_user.lower()+str(nb_machine)
|
initial['name'] = user.get_next_domain_name()
|
||||||
kwargs['initial'] = initial
|
kwargs['initial'] = initial
|
||||||
super(DomainForm, self).__init__(*args, **kwargs)
|
super(DomainForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -142,7 +145,7 @@ class DelMachineTypeForm(Form):
|
||||||
class IpTypeForm(ModelForm):
|
class IpTypeForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IpType
|
model = IpType
|
||||||
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'vlan']
|
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'prefix_v6', 'vlan']
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -151,7 +154,7 @@ class IpTypeForm(ModelForm):
|
||||||
|
|
||||||
class EditIpTypeForm(IpTypeForm):
|
class EditIpTypeForm(IpTypeForm):
|
||||||
class Meta(IpTypeForm.Meta):
|
class Meta(IpTypeForm.Meta):
|
||||||
fields = ['extension','type','need_infra', 'vlan']
|
fields = ['extension','type','need_infra', 'prefix_v6', 'vlan']
|
||||||
|
|
||||||
class DelIpTypeForm(Form):
|
class DelIpTypeForm(Form):
|
||||||
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
|
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
20
machines/migrations/0059_iptype_prefix_v6.py
Normal file
20
machines/migrations/0059_iptype_prefix_v6.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-10-02 16:33
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0058_auto_20171002_0350'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='prefix_v6',
|
||||||
|
field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -70,6 +70,7 @@ class IpType(models.Model):
|
||||||
need_infra = models.BooleanField(default=False)
|
need_infra = models.BooleanField(default=False)
|
||||||
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
|
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
|
||||||
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
|
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
|
||||||
|
prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True)
|
||||||
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
|
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -122,6 +123,9 @@ class IpType(models.Model):
|
||||||
for element in IpType.objects.all().exclude(pk=self.pk):
|
for element in IpType.objects.all().exclude(pk=self.pk):
|
||||||
if not self.ip_set.isdisjoint(element.ip_set):
|
if not self.ip_set.isdisjoint(element.ip_set):
|
||||||
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
|
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
|
||||||
|
# On formate le prefix v6
|
||||||
|
if self.prefix_v6:
|
||||||
|
self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network)
|
||||||
return
|
return
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -218,7 +222,6 @@ class Interface(models.Model):
|
||||||
PRETTY_NAME = "Interface"
|
PRETTY_NAME = "Interface"
|
||||||
|
|
||||||
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
#ipv6 = models.GenericIPAddressField(protocol='IPv6', null=True)
|
|
||||||
mac_address = MACAddressField(integer=False, unique=True)
|
mac_address = MACAddressField(integer=False, unique=True)
|
||||||
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
|
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
|
||||||
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
|
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
|
||||||
|
@ -232,6 +235,18 @@ class Interface(models.Model):
|
||||||
user = self.machine.user
|
user = self.machine.user
|
||||||
return machine.active and user.has_access()
|
return machine.active and user.has_access()
|
||||||
|
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def ipv6_object(self):
|
||||||
|
if self.type.ip_type.prefix_v6:
|
||||||
|
return EUI(self.mac_address).ipv6(IPNetwork(self.type.ip_type.prefix_v6).network)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def ipv6(self):
|
||||||
|
return str(self.ipv6_object)
|
||||||
|
|
||||||
def mac_bare(self):
|
def mac_bare(self):
|
||||||
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
|
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,25 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
def get_macaddress(self, obj):
|
def get_macaddress(self, obj):
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
|
def get_dns(self, obj):
|
||||||
|
return obj.domain.name
|
||||||
|
|
||||||
|
def get_interface_extension(self, obj):
|
||||||
|
return obj.domain.extension.name
|
||||||
|
|
||||||
|
def get_macaddress(self, obj):
|
||||||
|
return str(obj.mac_address)
|
||||||
|
|
||||||
class ExtensionNameField(serializers.RelatedField):
|
class ExtensionNameField(serializers.RelatedField):
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.name
|
return value.name
|
||||||
|
|
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<th>Nécessite l'autorisation infra</th>
|
<th>Nécessite l'autorisation infra</th>
|
||||||
<th>Début</th>
|
<th>Début</th>
|
||||||
<th>Fin</th>
|
<th>Fin</th>
|
||||||
|
<th>Préfixe v6</th>
|
||||||
<th>Sur vlan</th>
|
<th>Sur vlan</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -42,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ type.need_infra }}</td>
|
<td>{{ type.need_infra }}</td>
|
||||||
<td>{{ type.domaine_ip_start }}</td>
|
<td>{{ type.domaine_ip_start }}</td>
|
||||||
<td>{{ type.domaine_ip_stop }}</td>
|
<td>{{ type.domaine_ip_stop }}</td>
|
||||||
|
<td>{{ type.prefix_v6 }}</td>
|
||||||
<td>{{ type.vlan }}</td>
|
<td>{{ type.vlan }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% if is_infra %}
|
{% if is_infra %}
|
||||||
|
|
|
@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<th>Nom dns</th>
|
<th>Nom dns</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Mac</th>
|
<th>Mac</th>
|
||||||
<th>Ipv4</th>
|
<th>IP</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -74,7 +74,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</td>
|
</td>
|
||||||
<td>{{ interface.type }}</td>
|
<td>{{ interface.type }}</td>
|
||||||
<td>{{ interface.mac_address }}</td>
|
<td>{{ interface.mac_address }}</td>
|
||||||
<td>{{ interface.ipv4 }}</td>
|
<td>{{ interface.ipv4 }}
|
||||||
|
{% if ipv6_enabled %}
|
||||||
|
{{ interface.ipv6 }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
|
|
@ -41,7 +41,7 @@ from django.contrib.auth import authenticate, login
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
from machines.serializers import InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
|
from machines.serializers import FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from reversion.models import Version
|
from reversion.models import Version
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ def new_machine(request, userid):
|
||||||
machine = NewMachineForm(request.POST or None)
|
machine = NewMachineForm(request.POST or None)
|
||||||
interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
|
interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
|
||||||
nb_machine = Interface.objects.filter(machine__user=userid).count()
|
nb_machine = Interface.objects.filter(machine__user=userid).count()
|
||||||
domain = DomainForm(request.POST or None, name_user=user.pseudo.replace('_','-'), nb_machine=nb_machine)
|
domain = DomainForm(request.POST or None, user=user, nb_machine=nb_machine)
|
||||||
if machine.is_valid() and interface.is_valid():
|
if machine.is_valid() and interface.is_valid():
|
||||||
new_machine = machine.save(commit=False)
|
new_machine = machine.save(commit=False)
|
||||||
new_machine.user = user
|
new_machine.user = user
|
||||||
|
@ -1027,6 +1027,14 @@ def mac_ip_list(request):
|
||||||
seria = InterfaceSerializer(interfaces, many=True)
|
seria = InterfaceSerializer(interfaces, many=True)
|
||||||
return seria.data
|
return seria.data
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@login_required
|
||||||
|
@permission_required('serveur')
|
||||||
|
def full_mac_ip_list(request):
|
||||||
|
interfaces = all_active_assigned_interfaces()
|
||||||
|
seria = FullInterfaceSerializer(interfaces, many=True)
|
||||||
|
return seria.data
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('serveur')
|
@permission_required('serveur')
|
||||||
|
@ -1086,7 +1094,7 @@ def mac_ip(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('serveur')
|
@permission_required('serveur')
|
||||||
def mac_ip_dns(request):
|
def mac_ip_dns(request):
|
||||||
seria = mac_ip_list(request)
|
seria = full_mac_ip_list(request)
|
||||||
return JSONResponse(seria)
|
return JSONResponse(seria)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
|
20
preferences/migrations/0020_optionalmachine_ipv6.py
Normal file
20
preferences/migrations/0020_optionalmachine_ipv6.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-10-02 16:14
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0019_remove_optionaltopologie_mac_autocapture'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='optionalmachine',
|
||||||
|
name='ipv6',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -45,6 +45,7 @@ class OptionalMachine(models.Model):
|
||||||
password_machine = models.BooleanField(default=False)
|
password_machine = models.BooleanField(default=False)
|
||||||
max_lambdauser_interfaces = models.IntegerField(default=10)
|
max_lambdauser_interfaces = models.IntegerField(default=10)
|
||||||
max_lambdauser_aliases = models.IntegerField(default=10)
|
max_lambdauser_aliases = models.IntegerField(default=10)
|
||||||
|
ipv6 = models.BooleanField(default=False)
|
||||||
|
|
||||||
class OptionalTopologie(models.Model):
|
class OptionalTopologie(models.Model):
|
||||||
PRETTY_NAME = "Options topologie"
|
PRETTY_NAME = "Options topologie"
|
||||||
|
|
|
@ -72,6 +72,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<th>Alias dns autorisé par utilisateur</th>
|
<th>Alias dns autorisé par utilisateur</th>
|
||||||
<td>{{ machineoptions.max_lambdauser_aliases }}</td>
|
<td>{{ machineoptions.max_lambdauser_aliases }}</td>
|
||||||
|
<th>Support de l'ipv6</th>
|
||||||
|
<td>{{ machineoptions.ipv6 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h4>Préférences topologie</h4>
|
<h4>Préférences topologie</h4>
|
||||||
|
|
|
@ -23,10 +23,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from machines.models import Interface, Machine
|
from machines.models import Interface, Machine
|
||||||
from preferences.models import GeneralOption
|
from preferences.models import GeneralOption, OptionalMachine
|
||||||
|
|
||||||
def context_user(request):
|
def context_user(request):
|
||||||
general_options, created = GeneralOption.objects.get_or_create()
|
general_options, created = GeneralOption.objects.get_or_create()
|
||||||
|
machine_options, created = OptionalMachine.objects.get_or_create()
|
||||||
user = request.user
|
user = request.user
|
||||||
if user.is_authenticated():
|
if user.is_authenticated():
|
||||||
interfaces = user.user_interfaces()
|
interfaces = user.user_interfaces()
|
||||||
|
@ -54,4 +55,5 @@ def context_user(request):
|
||||||
'is_admin' : is_admin,
|
'is_admin' : is_admin,
|
||||||
'interfaces': interfaces,
|
'interfaces': interfaces,
|
||||||
'site_name': general_options.site_name,
|
'site_name': general_options.site_name,
|
||||||
|
'ipv6_enabled' : machine_options.ipv6,
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ DATABASES = {
|
||||||
'ENGINE': 'ldapdb.backends.ldap',
|
'ENGINE': 'ldapdb.backends.ldap',
|
||||||
'NAME': 'ldap://ldap_host_ip/',
|
'NAME': 'ldap://ldap_host_ip/',
|
||||||
'USER': 'ldap_dn',
|
'USER': 'ldap_dn',
|
||||||
|
# 'TLS': True,
|
||||||
'PASSWORD': 'SUPER_SECRET_LDAP',
|
'PASSWORD': 'SUPER_SECRET_LDAP',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||||
from django.core.validators import MinLengthValidator
|
from django.core.validators import MinLengthValidator
|
||||||
from topologie.models import Room
|
from topologie.models import Room
|
||||||
from cotisations.models import Cotisation, Facture, Paiement, Vente
|
from cotisations.models import Cotisation, Facture, Paiement, Vente
|
||||||
from machines.models import Domain, Interface, MachineType, Machine, Nas, MachineType, regen
|
from machines.models import Domain, Interface, MachineType, Machine, Nas, MachineType, Extension, regen
|
||||||
from preferences.models import GeneralOption, AssoOption, OptionalUser, OptionalMachine, MailMessageOption
|
from preferences.models import GeneralOption, AssoOption, OptionalUser, OptionalMachine, MailMessageOption
|
||||||
|
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
@ -473,7 +473,9 @@ class User(AbstractBaseUser):
|
||||||
interface_cible.clean()
|
interface_cible.clean()
|
||||||
machine_parent.clean()
|
machine_parent.clean()
|
||||||
domain = Domain()
|
domain = Domain()
|
||||||
domain.name = self.pseudo.replace('_','-').lower() + str(all_machines.count())
|
domain.name = self.get_next_domain_name()
|
||||||
|
domain.interface_parent = interface_cible
|
||||||
|
domain.clean()
|
||||||
machine_parent.save()
|
machine_parent.save()
|
||||||
interface_cible.machine = machine_parent
|
interface_cible.machine = machine_parent
|
||||||
interface_cible.save()
|
interface_cible.save()
|
||||||
|
@ -492,6 +494,23 @@ class User(AbstractBaseUser):
|
||||||
self.pwd_ntlm = hashNT(password)
|
self.pwd_ntlm = hashNT(password)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def get_next_domain_name(self):
|
||||||
|
"""Look for an available name for a new interface for
|
||||||
|
this user by trying "pseudo0", "pseudo1", "pseudo2", ...
|
||||||
|
"""
|
||||||
|
|
||||||
|
def simple_pseudo():
|
||||||
|
return self.pseudo.replace('_', '-').lower()
|
||||||
|
|
||||||
|
def composed_pseudo( n ):
|
||||||
|
return simple_pseudo() + str(n)
|
||||||
|
|
||||||
|
num = 0
|
||||||
|
while Domain.objects.filter(name=composed_pseudo(num)) :
|
||||||
|
num += 1
|
||||||
|
return composed_pseudo(num)
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.pseudo
|
return self.pseudo
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue