diff --git a/abonapp/forms.py b/abonapp/forms.py index 6275dfa..eb163dc 100644 --- a/abonapp/forms.py +++ b/abonapp/forms.py @@ -41,7 +41,7 @@ class AbonForm(forms.ModelForm): abon_group_queryset = None if abon_group_queryset is not None: self.fields['street'].queryset = abon_group_queryset - #if instance is not None and instance.is_dynamic_ip: + # if instance is not None and instance.is_dynamic_ip: # self.fields['ip_address'].widget.attrs['readonly'] = True username = forms.CharField(max_length=127, required=False, initial=generate_random_username, @@ -139,7 +139,7 @@ class ExportUsersForm(forms.Form): FIELDS_CHOICES = ( ('username', _('profile username')), ('fio', _('fio')), - #('ip_address', _('Ip Address')), + # ('ip_address', _('Ip Address')), ('description', _('Comment')), ('street__name', _('Street')), ('house', _('House')), @@ -160,7 +160,7 @@ class ExportUsersForm(forms.Form): class MarkersForm(forms.ModelForm): class Meta: model = models.Abon - fields = ('markers',) + fields = 'markers', def save(self, commit=True): instance = super(MarkersForm, self).save(commit=False) diff --git a/abonapp/models.py b/abonapp/models.py index 038cd5f..d710f85 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -138,8 +138,6 @@ class Abon(BaseAccount): ('can_add_ballance', _('fill account')), ('can_ping', _('Can ping')) ) - # TODO: Fix when duplicates already in database - # unique_together = ('device', 'dev_port') verbose_name = _('Abon') verbose_name_plural = _('Abons') ordering = ('fio',) @@ -251,6 +249,15 @@ class Abon(BaseAccount): def get_absolute_url(self): return resolve_url('abonapp:abon_home', self.group.id, self.username) + def add_lease(self, ip: str): + existed_client_ips = tuple(l.ip for l in self.ip_addresses.all()) + if ip not in existed_client_ips: + lease = IpLeaseModel.objects.create_from_ip(ip=ip) + if lease is None: + return 'Subnet not found' + self.ip_addresses.add(lease) + #self.save() + class PassportInfo(models.Model): series = models.CharField(_('Pasport serial'), max_length=4, validators=(validators.integer_validator,)) diff --git a/abonapp/templates/abonapp/editAbon.html b/abonapp/templates/abonapp/editAbon.html index f887adc..1bb32ef 100644 --- a/abonapp/templates/abonapp/editAbon.html +++ b/abonapp/templates/abonapp/editAbon.html @@ -220,10 +220,20 @@ {% endfor %} diff --git a/abonapp/templates/abonapp/modal_add_lease.html b/abonapp/templates/abonapp/modal_add_lease.html new file mode 100644 index 0000000..c0b7f42 --- /dev/null +++ b/abonapp/templates/abonapp/modal_add_lease.html @@ -0,0 +1,25 @@ +{% extends request.is_ajax|yesno:'nullcont.htm,abonapp/ext.htm' %} +{% load i18n %} +{% load bootstrap3 %} +{% block content %} +
{% csrf_token %} + + + + +
+{% endblock %} \ No newline at end of file diff --git a/abonapp/templates/abonapp/modal_current_networks.html b/abonapp/templates/abonapp/modal_current_networks.html new file mode 100644 index 0000000..d9d18a2 --- /dev/null +++ b/abonapp/templates/abonapp/modal_current_networks.html @@ -0,0 +1,26 @@ +{% load i18n %} + + + + + + + + + + + {% for net_item in networks %} + + + + + {% empty %} + + + + {% endfor %} + +
{% trans 'Network' %}{% trans 'Scope' %}
{{ net_item }}{{ net_item.get_scope }}
{% trans 'Available networks not found' %}
diff --git a/abonapp/urls.py b/abonapp/urls.py index cb81aa3..edca190 100644 --- a/abonapp/urls.py +++ b/abonapp/urls.py @@ -30,7 +30,8 @@ subscriber_patterns = [ url(r'^session/(?P\d+)/start$', views.user_session_toggle, {'action': 'start'}, name='user_session_start'), url(r'^periodic_pay$', views.add_edit_periodic_pay, name='add_periodic_pay'), url(r'^periodic_pay(?P\d+)/$', views.add_edit_periodic_pay, name='add_periodic_pay'), - url(r'^periodic_pay(?P\d+)/del/$', views.del_periodic_pay, name='del_periodic_pay') + url(r'^periodic_pay(?P\d+)/del/$', views.del_periodic_pay, name='del_periodic_pay'), + url(r'^lease/add/$', views.lease_add, name='lease_add') ] group_patterns = [ @@ -42,6 +43,7 @@ group_patterns = [ url(r'^street/add$', views.street_add, name='street_add'), url(r'^street/edit', views.street_edit, name='street_edit'), url(r'^street/(?P\d+)/delete$', views.street_del, name='street_del'), + url(r'^active_networks/$', views.active_nets, name='active_nets'), url(r'^(?P\w{1,127})/', include(subscriber_patterns)) ] diff --git a/abonapp/views.py b/abonapp/views.py index 0cd3765..e37698b 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -1,4 +1,5 @@ from typing import Dict, Optional +from datetime import datetime, date, timedelta from django.contrib.gis.shortcuts import render_to_text from django.core.exceptions import PermissionDenied, ValidationError from django.db import IntegrityError, ProgrammingError, transaction @@ -22,11 +23,12 @@ from agent import NasFailedResult, Transmitter, NasNetworkError from . import forms from . import models from devapp.models import Device, Port as DevPort -from datetime import datetime, date, timedelta from taskapp.models import Task from dialing_app.models import AsteriskCDR from statistics.models import getModel from group_app.models import Group +from ip_pool.models import IpLeaseModel, NetworkModel +from ip_pool.forms import LeaseForm from guardian.shortcuts import get_objects_for_user, assign_perm from guardian.decorators import permission_required_or_403 as permission_required from djing import ping @@ -545,7 +547,7 @@ def chgroup_tariff(request, gid): if request.method == 'POST': tr = request.POST.getlist('tr') grp.tariff_set.clear() - grp.tariff_set.add(*(int(d) for d in tr)) + grp.tariff_set.add(*tr) grp.save() messages.success(request, _('Successfully saved')) return redirect('abonapp:ch_group_tariff', gid) @@ -836,6 +838,15 @@ def street_del(request, gid, sid): return redirect('abonapp:people_list', gid) +@login_required +@permission_required('group_app.can_view_group', (Group, 'pk', 'gid')) +def active_nets(request, gid): + nets = NetworkModel.objects.filter(groups__id=gid) + return render(request, 'abonapp/modal_current_networks.html', { + 'networks': nets + }) + + @login_required @permission_required('abonapp.can_view_additionaltelephones') @permission_required('group_app.can_view_group', (Group, 'pk', 'gid')) @@ -1071,6 +1082,44 @@ def user_session_toggle(request, gid, uname, lease_id, action=None): return redirect('abonapp:abon_home', gid, uname) +@login_required +@lib.decorators.only_admins +@permission_required('change_abon') +def lease_add(request, gid, uname): + group = get_object_or_404(Group, pk=gid) + if request.method == 'POST': + frm = LeaseForm(request.POST) + if frm.is_valid(): + try: + abon = get_object_or_404(models.Abon, username=uname) + cleaned = frm.clean() + ip = cleaned.get('ip_addr') + is_dynamic = cleaned.get('is_dynamic') + network_id = cleaned.get('possible_networks') # str(int) + network = get_object_or_404(NetworkModel, pk=network_id) + lease = IpLeaseModel.objects.create_from_ip(ip, net=network, is_dynamic=is_dynamic) + abon.ip_addresses.add(lease) + messages.success(request, _('Ip lease has been created')) + return redirect('abonapp:abon_home', gid, uname) + except lib.DuplicateEntry as e: + messages.error(request, e) + else: + messages.error(request, _('Check form errors')) + else: + first_network = NetworkModel.objects.filter(groups=group).first() + if first_network is not None: + free_ip = IpLeaseModel.objects.get_free_ip(first_network) + initial = {'ip_addr': free_ip} + else: + initial = None + frm = LeaseForm(initial=initial) + return render(request, 'abonapp/modal_add_lease.html', { + 'form': frm, + 'group': group, + 'uname': uname + }) + + # API's @login_required @lib.decorators.only_admins diff --git a/agent/commands/dhcp.py b/agent/commands/dhcp.py index bed56f0..73fd293 100644 --- a/agent/commands/dhcp.py +++ b/agent/commands/dhcp.py @@ -18,19 +18,14 @@ def dhcp_commit(client_ip: str, client_mac: str, switch_mac: str, switch_port: i abon = Abon.objects.get(device=dev) if not abon.is_dynamic_ip: return 'User settings is not dynamic' - existed_client_ips = tuple(l.ip for l in abon.ip_addresses.all()) - if client_ip not in existed_client_ips: - lease = IpLeaseModel.objects.create_from_ip( - ip=client_ip, - ) - if lease is None: - return 'Subnet not found' - abon.ip_addresses.add(lease) - abon.save() + add_lease_result = abon.add_lease(client_ip) + if add_lease_result is None: if abon.is_access(): abon.sync_with_nas(created=False) else: - print('D:', 'User %s is not access to service' % abon.username) + return 'User %s is not access to service' % abon.username + else: + return add_lease_result except Abon.DoesNotExist: return "User with device with mac '%s' does not exist" % switch_mac except Device.DoesNotExist: diff --git a/devapp/forms.py b/devapp/forms.py index a023b8a..14b5961 100644 --- a/devapp/forms.py +++ b/devapp/forms.py @@ -3,6 +3,7 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from django.db import IntegrityError +from djing.lib import DuplicateEntry from djing.lib.tln.tln import ValidationError as TlnValidationError from . import models from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX @@ -82,6 +83,6 @@ class PortForm(forms.ModelForm): super(PortForm, self).save(commit) except IntegrityError as e: if "Duplicate entry" in str(e): - raise models.DeviceDBException(_('Port number on device must be unique')) + raise DuplicateEntry(_('Port number on device must be unique')) else: raise models.DeviceDBException(e) diff --git a/devapp/views.py b/devapp/views.py index 67a27b5..5a737b9 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -14,7 +14,7 @@ from django.views.generic import DetailView, DeleteView, UpdateView, CreateView from devapp.base_intr import DeviceImplementationError from djing.lib.decorators import only_admins, hash_auth_view -from djing.lib import safe_int, ProcessLocked +from djing.lib import safe_int, ProcessLocked, DuplicateEntry from abonapp.models import Abon from djing.lib.tln import ZteOltConsoleError, OnuZteRegisterError, ZteOltLoginFailed from group_app.models import Group @@ -386,7 +386,7 @@ def edit_single_port(request, group_id, device_id, port_id): }, request=request) except Port.DoesNotExist: messages.error(request, _('Port does not exist')) - except DeviceDBException as e: + except (DeviceDBException, DuplicateEntry) as e: messages.error(request, e) return redirect('devapp:manage_ports', group_id, device_id) @@ -416,7 +416,7 @@ def add_single_port(request, group_id, device_id): }, request=request) except Device.DoesNotExist: messages.error(request, _('Device does not exist')) - except DeviceDBException as e: + except (DeviceDBException, DuplicateEntry) as e: messages.error(request, e) return redirect('devapp:manage_ports', group_id, device_id) diff --git a/djing/lib/__init__.py b/djing/lib/__init__.py index 199547e..2474355 100644 --- a/djing/lib/__init__.py +++ b/djing/lib/__init__.py @@ -142,3 +142,9 @@ def process_lock(fn): if s is not None: s.close() return wrapped + +# +# Raises when IntegrityError in db +# +class DuplicateEntry(Exception): + pass diff --git a/ip_pool/fields.py b/ip_pool/fields.py index d09fc6e..bb52161 100644 --- a/ip_pool/fields.py +++ b/ip_pool/fields.py @@ -56,7 +56,7 @@ class GenericIpAddressWithPrefix(models.GenericIPAddressField): value = value.strip() if ':' in value: ip = ip_network(value) - return ip.compressed + return ip.with_prefixlen return value def get_db_prep_value(self, value, connection, prepared=False): diff --git a/ip_pool/forms.py b/ip_pool/forms.py index 60a0143..3cf989a 100644 --- a/ip_pool/forms.py +++ b/ip_pool/forms.py @@ -1,7 +1,8 @@ -from ipaddress import ip_network +from ipaddress import ip_network, ip_address from django import forms from django.core.exceptions import ValidationError +from django.utils.translation import ugettext as _ from ip_pool import models @@ -21,3 +22,32 @@ class NetworkForm(forms.ModelForm): class Meta: model = models.NetworkModel fields = '__all__' + + +class LeaseForm(forms.Form): + def __init__(self, data=None, *args, **kwargs): + super(LeaseForm, self).__init__(data=data, *args, **kwargs) + nets = models.NetworkModel.objects.defer('groups') + if nets.exists(): + self.fields['possible_networks'].choices = ((net.pk, str(net.get_network())) for net in nets.iterator()) + + def clean_ip_addr(self): + ip_addr = self.data.get('ip_addr') + if ip_addr is None: + return + ip_addr = ip_address(ip_addr) + net_id = self.data.get('possible_networks') + if net_id is None: + return ip_addr.compressed + net = models.NetworkModel.objects.get(pk=net_id) + if ip_addr not in net.get_network(): + raise ValidationError(_('Ip that you typed is not in subnet that you have selected')) + if ip_addr < ip_address(net.ip_start): + raise ValidationError(_('Ip that you have passed is less than allowed network range')) + if ip_addr > ip_address(net.ip_end): + raise ValidationError(_('Ip that you have passed is greater than allowed network range')) + return ip_addr.compressed + + ip_addr = forms.GenericIPAddressField(label=_('Ip address')) + is_dynamic = forms.BooleanField(label=_('Is dynamic'), required=False) + possible_networks = forms.ChoiceField(label=_('Possible networks')) diff --git a/ip_pool/models.py b/ip_pool/models.py index b16e606..cd9367e 100644 --- a/ip_pool/models.py +++ b/ip_pool/models.py @@ -1,15 +1,18 @@ from datetime import timedelta from ipaddress import ip_network, ip_address +from typing import AnyStr from django.conf import settings +from django.db.utils import IntegrityError from django.shortcuts import resolve_url from django.core.exceptions import ValidationError, ImproperlyConfigured from django.db import models from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ -#from djing.fields import MACAddressField +from djing.lib import DuplicateEntry from ip_pool.fields import GenericIpAddressWithPrefix +from group_app.models import Group class NetworkModel(models.Model): @@ -29,11 +32,19 @@ class NetworkModel(models.Model): ) kind = models.CharField(_('Kind of network'), max_length=6, choices=NETWORK_KINDS, default='guest') description = models.CharField(_('Description'), max_length=64) + groups = models.ManyToManyField(Group, verbose_name=_('Groups')) + + # Usable ip range + ip_start = models.GenericIPAddressField(_('Start work ip range')) + ip_end = models.GenericIPAddressField(_('End work ip range')) def __str__(self): - return "%s: %s" % (self.description, self.network) + netw = self.get_network() + return "%s: %s" % (self.description, netw.with_prefixlen) def get_network(self): + if self.network is None: + return if self._netw_cache is None: self._netw_cache = ip_network(self.network) return self._netw_cache @@ -41,35 +52,104 @@ class NetworkModel(models.Model): def get_absolute_url(self): return resolve_url('ip_pool:net_edit', self.pk) + def clean(self): + errs = {} + if self.network is None: + errs['network'] = ValidationError(_('Network is invalid'), code='invalid') + raise ValidationError(errs) + net = self.get_network() + if self.ip_start is None: + errs['ip_start'] = ValidationError(_('Ip start is invalid'), code='invalid') + raise ValidationError(errs) + start_ip = ip_address(self.ip_start) + if start_ip not in net: + errs['ip_start'] = ValidationError(_('Start ip must be in subnet of specified network'), code='invalid') + if self.ip_end is None: + errs['ip_end'] = ValidationError(_('Ip end is invalid'), code='invalid') + raise ValidationError(errs) + end_ip = ip_address(self.ip_end) + if end_ip not in net: + errs['ip_end'] = ValidationError(_('End ip must be in subnet of specified network'), code='invalid') + if errs: + raise ValidationError(errs) + + other_nets = NetworkModel.objects.exclude(pk=self.pk).only('network').order_by('network') + if not other_nets.exists(): + return + for onet in other_nets.iterator(): + onet_netw = onet.get_network() + if net.overlaps(onet_netw): + errs['network'] = ValidationError(_('Network is overlaps with %(other_network)s'), params={ + 'other_network': str(onet_netw) + }) + raise ValidationError(errs) + + def get_scope(self) -> AnyStr: + net = self.get_network() + if net.is_global: + return _('Global') + elif net.is_link_local: + return _('Link local') + elif net.is_loopback: + return _('Loopback') + elif net.is_multicast: + return _('Multicast') + elif net.is_private: + return _('Private') + elif net.is_reserved: + return _('Reserved') + elif net.is_site_local: + return _('Site local') + elif net.is_unspecified: + return _('Unspecified') + return "I don't know" + class Meta: db_table = 'ip_pool_network' verbose_name = _('Network') verbose_name_plural = _('Networks') - ordering = ('description',) + ordering = ('network',) class IpLeaseManager(models.Manager): def get_free_ip(self, network: NetworkModel): - netw = ip_network(network) - employed_ip_queryset = self.filter(network=network) - free_ip = next(ip_address(net) for ip, net in zip( - employed_ip_queryset, netw - ) if ip != net) - return free_ip - - def create_from_ip(self, ip: str, cidr_subnet: int): - # FIXME: get subnet - raise NotImplementedError - net = ip_network((ip, cidr_subnet), strict=False) - netw_instance = NetworkModel.objects.filter(network=str(net)).first() - if netw_instance is not None: + netw = network.get_network() + work_range_start_ip = ip_address(network.ip_start) + work_range_end_ip = ip_address(network.ip_end) + employed_ip_queryset = self.filter(network=network, is_dynamic=False).order_by('ip').only('ip') + + if employed_ip_queryset.exists(): + used_ip_gen = employed_ip_queryset.iterator() + for net_ip in netw.hosts(): + if net_ip < work_range_start_ip: + continue + elif net_ip > work_range_end_ip: + break + used_ip = next(used_ip_gen, None) + if used_ip is None: + return net_ip + ip = ip_address(used_ip.ip) + if net_ip < ip: + return net_ip + else: + for net in netw.hosts(): + if work_range_start_ip <= net <= work_range_end_ip: + return net + + def create_from_ip(self, ip: str, net: NetworkModel, is_dynamic=True): + # ip = ip_address(ip) + try: return self.create( ip=ip, - network=netw_instance, - is_dynamic=True, + network=net, + is_dynamic=is_dynamic, is_active=True ) + except IntegrityError as e: + if 'Duplicate entry' in str(e): + raise DuplicateEntry(_('Ip has already taken')) + raise e def expired(self): lease_live_time = getattr(settings, 'LEASE_LIVE_TIME') @@ -104,9 +184,8 @@ class IpLeaseModel(models.Model): def clean(self): ip = ip_address(self.ip) network = self.network.get_network() - if ip not in network: - raise ValidationError(_('Ip address %(ip)s not in %(net)s network') % { + raise ValidationError(_('Ip address %(ip)s not in %(net)s network'), params={ 'ip': ip, 'net': network }, code='invalid') diff --git a/ip_pool/templates/ip_pool/ip_leases_list.html b/ip_pool/templates/ip_pool/ip_leases_list.html index 831578e..078fd74 100644 --- a/ip_pool/templates/ip_pool/ip_leases_list.html +++ b/ip_pool/templates/ip_pool/ip_leases_list.html @@ -28,7 +28,7 @@ {{ ip.ip }} {{ ip.lease_time|date:'j:n H:i:s' }} - {{ ip.network }} + {{ ip.get_network }} {% empty %} diff --git a/ip_pool/templates/ip_pool/net_edit.html b/ip_pool/templates/ip_pool/net_edit.html index 661ef1e..c2b27d5 100644 --- a/ip_pool/templates/ip_pool/net_edit.html +++ b/ip_pool/templates/ip_pool/net_edit.html @@ -31,6 +31,10 @@ + + + + {% trans 'Back' %} @@ -38,6 +42,10 @@ + + + + diff --git a/ip_pool/templates/ip_pool/network_groups_available.html b/ip_pool/templates/ip_pool/network_groups_available.html new file mode 100644 index 0000000..c12af06 --- /dev/null +++ b/ip_pool/templates/ip_pool/network_groups_available.html @@ -0,0 +1,33 @@ +{% extends request.is_ajax|yesno:'bajax.html,base.html' %} +{% load i18n %} + +{% block breadcrumb %} + +{% endblock %} + +{% block page-header %} +

{% trans 'Make that pool available in specified groups' %}

+{% endblock %} + +{% block main %} +
{% csrf_token %} + {% for group in groups %} +
+ +
+ {% endfor %} + +
+{% endblock %} diff --git a/ip_pool/templates/ip_pool/network_list.html b/ip_pool/templates/ip_pool/network_list.html index 796753d..e386a59 100644 --- a/ip_pool/templates/ip_pool/network_list.html +++ b/ip_pool/templates/ip_pool/network_list.html @@ -19,17 +19,19 @@ {% trans 'Network' %} {% trans 'Kind' %} - {% trans 'Description' %} + {% trans 'Description' %} + {% trans 'Scope' %} - {% with can_ch_net=perms.ip_pool.change_networkmodel %} + {% with can_ch_net=perms.ip_pool.change_networkmodel can_del_net=perms.ip_poo.delete_networkmodel %} {% for netw in networks_list %} - {{ netw }} + {{ netw.get_network }} {{ netw.get_kind_display }} {{ netw.description }} + {{ netw.get_scope }} {% if can_ch_net %} @@ -42,6 +44,17 @@ {% endif %} + {% if can_del_net %} + + + + + {% else %} + + + + + {% endif %} @@ -50,14 +63,14 @@ {% empty %} - {% trans 'You have not any networks available' %} + {% trans 'You have not any networks available' %} {% endfor %} {% endwith %} - + {% trans 'Add' %} diff --git a/ip_pool/templates/ip_pool/networkmodel_confirm_delete.html b/ip_pool/templates/ip_pool/networkmodel_confirm_delete.html new file mode 100644 index 0000000..b9c7d1c --- /dev/null +++ b/ip_pool/templates/ip_pool/networkmodel_confirm_delete.html @@ -0,0 +1,19 @@ +{% extends 'base_delete_modal.html' %} +{% load i18n %} + +{% block modal_form_url %} + {% url 'ip_pool:net_delete' object.pk %} +{% endblock %} + +{% block modal_form_title %} + {% trans 'Remove network' %} +{% endblock %} + +{% block modal_form_text %} +

{% blocktrans trimmed with network_name=object %} + To delete network '{{ network_name }}'? + {% endblocktrans %}

+

{% blocktrans trimmed %} + Attention! All leases in that network will be removed and services finished. + {% endblocktrans %}

+{% endblock %} diff --git a/ip_pool/urls.py b/ip_pool/urls.py index 984dbaf..2c0303a 100644 --- a/ip_pool/urls.py +++ b/ip_pool/urls.py @@ -8,7 +8,9 @@ urlpatterns = [ url('^$', views.NetworksListView.as_view(), name='networks'), url('^network_add/$', views.NetworkCreateView.as_view(), name='net_add'), url('^(?P\d{1,6})/$', views.IpLeasesListView.as_view(), name='ip_leases_list'), - url('^(?P\d{1,6})/edit$', views.NetworkUpdateView.as_view(), name='net_edit'), + url('^(?P\d{1,6})/edit/$', views.NetworkUpdateView.as_view(), name='net_edit'), + url('^(?P\d{1,6})/del/$', views.NetworkDeleteView.as_view(), name='net_delete'), + url('^(?P\d{1,6})/group_attach/$', views.network_in_groups, name='net_groups') ] for dev_kind_code, _ in models.NetworkModel.NETWORK_KINDS: diff --git a/ip_pool/views.py b/ip_pool/views.py index 91bca2c..b4e77e6 100644 --- a/ip_pool/views.py +++ b/ip_pool/views.py @@ -1,13 +1,15 @@ from django.contrib.auth.decorators import login_required from django.contrib import messages -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ -from django.views.generic import UpdateView, CreateView +from django.views.generic import UpdateView, CreateView, DeleteView from guardian.decorators import permission_required_or_403 as permission_required from djing.global_base_views import OrderedFilteredList from ip_pool import models, forms +from group_app.models import Group @method_decorator(login_required, name='dispatch') @@ -38,6 +40,18 @@ class NetworkUpdateView(UpdateView): return r +@method_decorator(login_required, name='dispatch') +@method_decorator(permission_required('ip_pool.delete_networkmodel'), name='dispatch') +class NetworkDeleteView(DeleteView): + model = models.NetworkModel + pk_url_kwarg = 'net_id' + success_url = reverse_lazy('ip_pool:networks') + + def delete(self, request, *args, **kwargs): + messages.success(request, _('Network has been deleted')) + return super(NetworkDeleteView, self).delete(request, *args, **kwargs) + + @method_decorator(login_required, name='dispatch') class IpLeasesListView(OrderedFilteredList): template_name = 'ip_pool/ip_leases_list.html' @@ -65,3 +79,22 @@ class NetworkCreateView(CreateView): r = super().form_valid(form) messages.success(self.request, _('Network has been created')) return r + + +@login_required +def network_in_groups(request, net_id): + network = get_object_or_404(models.NetworkModel, pk=net_id) + if request.method == 'POST': + gr = request.POST.getlist('gr') + network.groups.clear() + network.groups.add(*gr) + network.save() + messages.success(request, _('Successfully saved')) + return redirect('ip_pool:net_groups', net_id) + + selected_grps = tuple(pk[0] for pk in network.groups.only('pk').values_list('pk')) + return render(request, 'ip_pool/network_groups_available.html', { + 'object': network, + 'selected_grps': selected_grps, + 'groups': Group.objects.all().iterator() + })