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 %}
+
+{% 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 %}
+
+
+
+
+
+ {% trans 'Network' %}
+ {% trans 'Scope' %}
+
+
+
+ {% for net_item in networks %}
+
+ {{ net_item }}
+ {{ net_item.get_scope }}
+
+ {% empty %}
+
+ {% trans 'Available networks not found' %}
+
+ {% endfor %}
+
+
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 'Save' %}
+
+
+ {% trans 'Remove' %}
+
{% trans 'Back' %}
@@ -38,6 +42,10 @@
{% trans 'View employed' %}
+
+
+ {% trans 'Groups available' %}
+
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 %}
+
+
+ {% trans 'Ip pool' %}
+ {{ object }}
+ {% trans 'Belonging networks for groups' %}
+
+{% endblock %}
+
+{% block page-header %}
+ {% trans 'Make that pool available in specified groups' %}
+{% endblock %}
+
+{% block main %}
+
+{% 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 @@
{% trans 'Edit' %}
{% endif %}
+ {% if can_del_net %}
+
+
+ {% trans 'Remove' %}
+
+ {% else %}
+
+
+ {% trans 'Permission denied' %}
+
+ {% endif %}
{% trans 'View employed' %}
@@ -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()
+ })