diff --git a/api/serializers.py b/api/serializers.py index e7b23f32..af92e0d0 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -811,7 +811,8 @@ class SwitchPortSerializer(serializers.ModelSerializer): model = topologie.Switch fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'interfaces_subnet', 'interfaces6_subnet', 'automatic_provision', 'rest_enabled', - 'web_management_enabled', 'get_radius_key_value', 'get_management_cred_value') + 'web_management_enabled', 'get_radius_key_value', 'get_management_cred_value', + 'list_modules') # LOCAL EMAILS diff --git a/machines/migrations/0098_auto_20190102_1745.py b/machines/migrations/0098_auto_20190102_1745.py new file mode 100644 index 00000000..e886e8a1 --- /dev/null +++ b/machines/migrations/0098_auto_20190102_1745.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-01-02 23:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0097_extension_dnssec'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='specific_role', + field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursif-server', 'Recursive DNS server'), ('dns-recursive-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True), + ), + ] diff --git a/machines/migrations/0099_role_recursive_dns.py b/machines/migrations/0099_role_recursive_dns.py new file mode 100644 index 00000000..c1ce3965 --- /dev/null +++ b/machines/migrations/0099_role_recursive_dns.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-01-02 23:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def migrate(apps, schema_editor): + Role = apps.get_model('machines', 'Role') + + for role in Role.objects.filter(specific_role='dns-recursif-server'): + role.specific_role = 'dns-recursive-server' + role.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0098_auto_20190102_1745'), + ] + + operations = [ + migrations.RunPython(migrate), + ] + + diff --git a/machines/migrations/0100_auto_20190102_1753.py b/machines/migrations/0100_auto_20190102_1753.py new file mode 100644 index 00000000..35f7b78d --- /dev/null +++ b/machines/migrations/0100_auto_20190102_1753.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-01-02 23:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0099_role_recursive_dns'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='specific_role', + field=models.CharField(blank=True, choices=[('dhcp-server', 'DHCP server'), ('switch-conf-server', 'Switches configuration server'), ('dns-recursive-server', 'Recursive DNS server'), ('ntp-server', 'NTP server'), ('radius-server', 'RADIUS server'), ('log-server', 'Log server'), ('ldap-master-server', 'LDAP master server'), ('ldap-backup-server', 'LDAP backup server'), ('smtp-server', 'SMTP server'), ('postgresql-server', 'postgreSQL server'), ('mysql-server', 'mySQL server'), ('sql-client', 'SQL client'), ('gateway', 'Gateway')], max_length=32, null=True), + ), + ] diff --git a/machines/models.py b/machines/models.py index 1cf840d7..a4685676 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1612,7 +1612,7 @@ class Role(RevMixin, AclMixin, models.Model): ROLE = ( ('dhcp-server', _("DHCP server")), ('switch-conf-server', _("Switches configuration server")), - ('dns-recursif-server', _("Recursive DNS server")), + ('dns-recursive-server', _("Recursive DNS server")), ('ntp-server', _("NTP server")), ('radius-server', _("RADIUS server")), ('log-server', _("Log server")), diff --git a/preferences/models.py b/preferences/models.py index 4644aa1c..104c261d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -278,12 +278,13 @@ class OptionalTopologie(AclMixin, PreferencesModel): log_servers = Role.all_interfaces_for_roletype("log-server").filter(type__ip_type=self.switchs_ip_type) radius_servers = Role.all_interfaces_for_roletype("radius-server").filter(type__ip_type=self.switchs_ip_type) dhcp_servers = Role.all_interfaces_for_roletype("dhcp-server") + dns_recursive_servers = Role.all_interfaces_for_roletype("dns-recursive-server").filter(type__ip_type=self.switchs_ip_type) subnet = None subnet6 = None if self.switchs_ip_type: subnet = self.switchs_ip_type.ip_set_full_info subnet6 = self.switchs_ip_type.ip6_set_full_info - return {'ntp_servers': return_ips_dict(ntp_servers), 'log_servers': return_ips_dict(log_servers), 'radius_servers': return_ips_dict(radius_servers), 'dhcp_servers': return_ips_dict(dhcp_servers), 'subnet': subnet, 'subnet6': subnet6} + return {'ntp_servers': return_ips_dict(ntp_servers), 'log_servers': return_ips_dict(log_servers), 'radius_servers': return_ips_dict(radius_servers), 'dhcp_servers': return_ips_dict(dhcp_servers), 'dns_recursive_servers': return_ips_dict(dns_recursive_servers), 'subnet': subnet, 'subnet6': subnet6} @cached_property def provision_switchs_enabled(self): diff --git a/topologie/forms.py b/topologie/forms.py index fa089507..ed6fa5b9 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -55,6 +55,8 @@ from .models import ( SwitchBay, Building, PortProfile, + ModuleSwitch, + ModuleOnSwitch, ) @@ -269,3 +271,23 @@ class EditPortProfileForm(FormRevMixin, ModelForm): prefix=prefix, **kwargs) +class EditModuleForm(FormRevMixin, ModelForm): + """Add and edit module instance""" + class Meta: + model = ModuleSwitch + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditModuleForm, self).__init__(*args, prefix=prefix, **kwargs) + + +class EditSwitchModuleForm(FormRevMixin, ModelForm): + """Add/edit a switch to a module""" + class Meta: + model = ModuleOnSwitch + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditSwitchModuleForm, self).__init__(*args, prefix=prefix, **kwargs) diff --git a/topologie/migrations/0067_auto_20181230_1819.py b/topologie/migrations/0067_auto_20181230_1819.py new file mode 100644 index 00000000..57f268ea --- /dev/null +++ b/topologie/migrations/0067_auto_20181230_1819.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-12-30 17:19 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0066_modelswitch_commercial_name'), + ] + + operations = [ + migrations.CreateModel( + name='ModuleOnSwitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slot', models.CharField(help_text='Slot on switch', max_length=15, verbose_name='Slot')), + ], + options={ + 'verbose_name': 'link between switchs and modules', + 'permissions': (('view_moduleonswitch', 'Can view a moduleonswitch object'),), + }, + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), + ), + migrations.CreateModel( + name='ModuleSwitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reference', models.CharField(help_text='Reference of a module', max_length=255, verbose_name='Module reference')), + ('comment', models.CharField(blank=True, help_text='Comment', max_length=255, null=True, verbose_name='Comment')), + ], + options={ + 'verbose_name': 'Module of a switch', + 'permissions': (('view_moduleswitch', 'Can view a module object'),), + }, + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), + ), + migrations.AddField( + model_name='modelswitch', + name='is_itself_module', + field=models.BooleanField(default=False, help_text='Does the switch, itself, considered as a module'), + ), + migrations.AddField( + model_name='modelswitch', + name='is_modular', + field=models.BooleanField(default=False, help_text='Is this switch model modular'), + ), + migrations.AddField( + model_name='moduleonswitch', + name='module', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='topologie.ModuleSwitch'), + ), + migrations.AddField( + model_name='moduleonswitch', + name='switch', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='topologie.Switch'), + ), + migrations.AlterUniqueTogether( + name='moduleonswitch', + unique_together=set([('slot', 'switch')]), + ), + ] diff --git a/topologie/migrations/0068_auto_20190102_1758.py b/topologie/migrations/0068_auto_20190102_1758.py new file mode 100644 index 00000000..b03e7ae5 --- /dev/null +++ b/topologie/migrations/0068_auto_20190102_1758.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-01-02 23:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0067_auto_20181230_1819'), + ] + + operations = [ + migrations.AlterField( + model_name='modelswitch', + name='is_itself_module', + field=models.BooleanField(default=False, help_text='Is the switch, itself, considered as a module'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index e08a1b21..e05fa50e 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -252,6 +252,7 @@ class Switch(AclMixin, Machine): help_text='Provision automatique de ce switch', ) + class Meta: unique_together = ('stack', 'stack_member_id') permissions = ( @@ -367,6 +368,17 @@ class Switch(AclMixin, Machine): """Return dict ip6:subnet for all ipv6 of the switch""" return dict((str(interface.ipv6().first()), interface.type.ip_type.ip6_set_full_info) for interface in self.interface_set.all()) + @cached_property + def list_modules(self): + """Return modules of that switch, list of dict (rank, reference)""" + modules = [] + if getattr(self.model, 'is_modular', None): + if self.model.is_itself_module: + modules.append((1, self.model.reference)) + for module_of_self in self.moduleonswitch_set.all(): + modules.append((module_of_self.slot, module_of_self.module.reference)) + return modules + def __str__(self): return str(self.get_name) @@ -389,6 +401,14 @@ class ModelSwitch(AclMixin, RevMixin, models.Model): null=True, blank=True ) + is_modular = models.BooleanField( + default=False, + help_text=_("Is this switch model modular"), + ) + is_itself_module = models.BooleanField( + default=False, + help_text=_("Is the switch, itself, considered as a module"), + ) class Meta: permissions = ( @@ -404,6 +424,53 @@ class ModelSwitch(AclMixin, RevMixin, models.Model): return str(self.constructor) + ' ' + self.reference +class ModuleSwitch(AclMixin, RevMixin, models.Model): + """A module of a switch""" + reference = models.CharField( + max_length=255, + help_text=_("Reference of a module"), + verbose_name=_("Module reference") + ) + comment = models.CharField( + max_length=255, + null=True, + blank=True, + help_text=_("Comment"), + verbose_name=_("Comment") + ) + + class Meta: + permissions = ( + ("view_moduleswitch", _("Can view a module object")), + ) + verbose_name = _("Module of a switch") + + + def __str__(self): + return str(self.reference) + + +class ModuleOnSwitch(AclMixin, RevMixin, models.Model): + """Link beetween module and switch""" + module = models.ForeignKey('ModuleSwitch', on_delete=models.CASCADE) + switch = models.ForeignKey('Switch', on_delete=models.CASCADE) + slot = models.CharField( + max_length=15, + help_text=_("Slot on switch"), + verbose_name=_("Slot") + ) + + class Meta: + permissions = ( + ("view_moduleonswitch", _("Can view a moduleonswitch object")), + ) + verbose_name = _("link between switchs and modules") + unique_together = ['slot', 'switch'] + + def __str__(self): + return 'On slot ' + str(self.slot) + ' of ' + str(self.switch) + + class ConstructorSwitch(AclMixin, RevMixin, models.Model): """Un constructeur de switch""" diff --git a/topologie/templates/topologie/aff_modules.html b/topologie/templates/topologie/aff_modules.html new file mode 100644 index 00000000..0c7a3207 --- /dev/null +++ b/topologie/templates/topologie/aff_modules.html @@ -0,0 +1,110 @@ +{% 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 %} +{% load logs_extra %} +{% load i18n %} + +{% if module_list.paginator %} +{% include "pagination.html" with list=module_list %} +{% endif %} + +
{% trans "Reference" %} | +{% trans "Comment" %} | +{% trans "Switchs" %} | ++ |
---|---|---|---|
{{ module.reference }} | +{{ module.comment }} | +
+ {% for module_switch in module.moduleonswitch_set.all %}
+ Slot {{ module_switch.slot }} of {{ module_switch.switch }}
+ {% can_edit module_switch %}
+
+
+
+ {% acl_end %}
+ {% can_delete module_switch %}
+
+
+
+ {% acl_end %}
+ + {% endfor %} + |
+ + {% can_edit module %} + + + + + + + {% acl_end %} + {% history_button module %} + {% can_delete module %} + + + + {% acl_end %} + | +
{% trans "Switch" %} | +{% trans "Reference" %} | +{% trans "Slot" %} | + + {% for switch in modular_switchs %} + {% if switch.list_modules %} +|
---|---|---|---|
+ {{ switch }} + | +|||
+ | {{ module.1 }} | +{{ module.0 }} | +