75 changed files with 1067 additions and 374 deletions
-
103abonapp/migrations/0002_ip_address_change.py
-
7abonapp/models.py
-
7abonapp/templates/abonapp/addAbon.html
-
7abonapp/templates/abonapp/addInvoice.html
-
14abonapp/templates/abonapp/debtors.html
-
2abonapp/templates/abonapp/editAbon.html
-
21abonapp/templates/abonapp/ext.htm
-
12abonapp/templates/abonapp/fin_report.html
-
11abonapp/templates/abonapp/group_list.html
-
10abonapp/templates/abonapp/group_tariffs.html
-
12abonapp/templates/abonapp/invoiceForPayment.html
-
12abonapp/templates/abonapp/log.html
-
9abonapp/templates/abonapp/peoples.html
-
17abonapp/views.py
-
3accounts_app/locale/ru/LC_MESSAGES/django.po
-
3accounts_app/models.py
-
12accounts_app/templates/accounts/acc_list.html
-
7accounts_app/templates/accounts/create_acc.html
-
9accounts_app/templates/accounts/ext.htm
-
11accounts_app/templates/accounts/perms/objects_of_type.html
-
9accounts_app/templates/accounts/perms/objects_types.html
-
9accounts_app/templates/accounts/perms/perms_edit.html
-
10accounts_app/templates/accounts/settings/ext.htm
-
2clientsideapp/templates/clientsideapp/index.html
-
44devapp/migrations/0005_device_ip_address_change.py
-
8devapp/models.py
-
15devapp/templates/devapp/add_dev.html
-
14devapp/templates/devapp/devices.html
-
12devapp/templates/devapp/devices_null_group.html
-
44devapp/templates/devapp/ext.htm
-
128devapp/templates/devapp/fix_dev_group.html
-
9devapp/templates/devapp/group_list.html
-
22devapp/templates/devapp/manage_ports/add_ports.html
-
13devapp/templates/devapp/manage_ports/fix_abon_device.html
-
11devapp/views.py
-
15dialing_app/templates/ext.html
-
4djing/global_base_views.py
-
6djing/settings.py
-
13djing/templatetags/globaltags.py
-
10djing/urls.py
-
14group_app/templates/group_app/ext.html
-
11group_app/templates/group_app/group_list.html
-
0ip_pool/__init__.py
-
5ip_pool/admin.py
-
5ip_pool/apps.py
-
30ip_pool/forms.py
-
56ip_pool/migrations/0001_initial.py
-
0ip_pool/migrations/__init__.py
-
91ip_pool/models.py
-
39ip_pool/templates/ip_pool/employed_ip_list.html
-
30ip_pool/templates/ip_pool/ext.html
-
35ip_pool/templates/ip_pool/net_add.html
-
41ip_pool/templates/ip_pool/net_edit.html
-
70ip_pool/templates/ip_pool/network_list.html
-
3ip_pool/tests.py
-
11ip_pool/urls.py
-
60ip_pool/views.py
-
7locale/ru/LC_MESSAGES/django.po
-
24mapapp/templates/maps/add_device.html
-
33mapapp/templates/maps/dot.html
-
11mapapp/templates/maps/options.html
-
11msg_app/templates/msg_app/chat.html
-
13msg_app/templates/msg_app/conversations.html
-
3searchapp/locale/ru/LC_MESSAGES/django.po
-
11searchapp/templates/searchapp/index.html
-
7tariff_app/templates/tariff_app/editTarif.html
-
9tariff_app/templates/tariff_app/ext.html
-
24tariff_app/templates/tariff_app/periodic_pays/add_edit.html
-
6taskapp/templates/taskapp/add_edit_task.html
-
16taskapp/templates/taskapp/ext.htm
-
16taskapp/templates/taskapp/tasklist_all.html
-
4taskapp/templates/taskapp/tasklist_empty.html
-
4taskapp/templates/taskapp/tasklist_failed.html
-
7taskapp/views.py
-
45templates/base.html
@ -0,0 +1,103 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.11 on 2018-06-12 12:05 |
|||
from __future__ import unicode_literals |
|||
import os |
|||
import re |
|||
from json import load |
|||
|
|||
import django.core.validators |
|||
from django.db import migrations, models |
|||
from django.core import serializers |
|||
|
|||
|
|||
TMP_FILE = '/tmp/djing_ip_field_abonapp_migrate.json' |
|||
|
|||
|
|||
def backup_info(apps, _): |
|||
Abon = apps.get_model('abonapp', 'Abon') |
|||
obs = Abon.objects.all() |
|||
with open(TMP_FILE, 'w') as f: |
|||
serializers.serialize('json', obs, stream=f) |
|||
|
|||
|
|||
def restore_info_to_new_scheme(apps, _): |
|||
Abon = apps.get_model('abonapp', 'Abon') |
|||
with open(TMP_FILE, 'r') as f: |
|||
for abon in load(f): |
|||
Abon.objects.filter(pk=abon['pk']).update(ip_address=abon['fields']['ip_address']) |
|||
if os.path.isfile(TMP_FILE): |
|||
os.remove(TMP_FILE) |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('abonapp', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunPython(backup_info), |
|||
migrations.AlterModelOptions( |
|||
name='abon', |
|||
options={'ordering': ('fio',), 'permissions': (('can_buy_tariff', 'Buy service perm'), ('can_view_passport', 'Can view passport'), ('can_add_ballance', 'fill account'), ('can_ping', 'Can ping')), 'verbose_name': 'Abon', 'verbose_name_plural': 'Abons'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='abonlog', |
|||
options={'ordering': ('-date',), 'permissions': (('can_view_abonlog', 'Can view subscriber logs'),)}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='abonstreet', |
|||
options={'ordering': ('name',), 'verbose_name': 'Street', 'verbose_name_plural': 'Streets'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='abontariff', |
|||
options={'ordering': ('time_start',), 'permissions': (('can_complete_service', 'finish service perm'),), 'verbose_name': 'Abon service', 'verbose_name_plural': 'Abon services'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='allpaylog', |
|||
options={'ordering': ('-date_action',)}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='alltimepaylog', |
|||
options={'ordering': ('-date_add',)}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='passportinfo', |
|||
options={'ordering': ('series',), 'verbose_name': 'Passport Info', 'verbose_name_plural': 'Passport Info'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='periodicpayforid', |
|||
options={'ordering': ('last_pay',)}, |
|||
), |
|||
migrations.AlterField( |
|||
model_name='abon', |
|||
name='ip_address', |
|||
field=models.GenericIPAddressField(blank=True, null=True, verbose_name='Ip Address'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='additionaltelephone', |
|||
name='telephone', |
|||
field=models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^(\\+[7,8,9,3]\\d{10,11})?$')], verbose_name='Telephone'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='passportinfo', |
|||
name='abon', |
|||
field=models.OneToOneField(blank=True, null=True, on_delete=models.deletion.CASCADE, to='abonapp.Abon'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='passportinfo', |
|||
name='distributor', |
|||
field=models.CharField(max_length=64, verbose_name='Distributor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='passportinfo', |
|||
name='number', |
|||
field=models.CharField(max_length=6, validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z', 32), code='invalid', message='Enter a valid integer.')], verbose_name='Pasport number'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='passportinfo', |
|||
name='series', |
|||
field=models.CharField(max_length=4, validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z', 32), code='invalid', message='Enter a valid integer.')], verbose_name='Pasport serial'), |
|||
), |
|||
migrations.RunPython(restore_info_to_new_scheme) |
|||
] |
|||
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.11 on 2018-06-12 11:24 |
|||
from __future__ import unicode_literals |
|||
import os |
|||
from json import load |
|||
|
|||
from django.db import migrations, models |
|||
from django.core import serializers |
|||
|
|||
|
|||
TMP_FILE = '/tmp/djing_ip_field_devapp_migrate.json' |
|||
|
|||
|
|||
def device_backup_info(apps, _): |
|||
Device = apps.get_model('devapp', 'Device') |
|||
obs = Device.objects.all() |
|||
with open(TMP_FILE, 'w') as f: |
|||
serializers.serialize('json', obs, stream=f) |
|||
|
|||
|
|||
def device_restore_info_to_new_scheme(apps, _): |
|||
Device = apps.get_model('devapp', 'Device') |
|||
with open(TMP_FILE, 'r') as f: |
|||
for device in load(f): |
|||
Device.objects.filter(pk=device['pk']).update(ip_address=device['fields']['ip_address']) |
|||
if os.path.isfile(TMP_FILE): |
|||
os.remove(TMP_FILE) |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('devapp', '0004_device_extra_data'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunPython(device_backup_info), |
|||
migrations.AlterField( |
|||
model_name='device', |
|||
name='ip_address', |
|||
field=models.GenericIPAddressField(blank=True, null=True, protocol='ipv4', verbose_name='Ip address'), |
|||
), |
|||
migrations.RunPython(device_restore_info_to_new_scheme) |
|||
] |
|||
@ -1,75 +1,73 @@ |
|||
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
|||
{% load i18n %} |
|||
{% load bootstrap3 %} |
|||
{% block main %} |
|||
|
|||
|
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li> |
|||
<li class="active">{{ dev.comment }}</li> |
|||
</ol> |
|||
|
|||
{% include 'message_block.html' %} |
|||
|
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"> |
|||
<h3 class="panel-title">{% trans 'Fix device group' %}</h3> |
|||
</div> |
|||
<div class="panel-body"> |
|||
|
|||
<form role="form" action="{% url 'devapp:fix_device_group' dev.pk %}" method="post">{% csrf_token %} |
|||
|
|||
{% bootstrap_icon 'globe' as ic %} |
|||
{% bootstrap_field form.ip_address addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'globe' as ic %} |
|||
{% bootstrap_field form.mac_addr addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'comment' as ic %} |
|||
{% bootstrap_field form.comment addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'hdd' as ic %} |
|||
{% bootstrap_field form.devtype addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'lock' as ic %} |
|||
{% bootstrap_field form.man_passw addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'subscript' as ic %} |
|||
{% bootstrap_field form.group addon_before=ic %} |
|||
|
|||
<div class="form-group"> |
|||
<label for="id_parent_dev">{% trans 'Parent device' %}</label> |
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li> |
|||
<li class="active">{{ dev.comment }}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
<div class="input-group selectajax" data-dst="/dev/search_dev"> |
|||
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span> |
|||
<input type="hidden" name="parent_dev" class="selectajax-hid" {% if selected_parent_dev %} |
|||
value="{{ selected_parent_dev.pk }}" {% endif %}> |
|||
{% if selected_parent_dev %} |
|||
<button class="selectajax-btn form-control btn btn-default">{{ selected_parent_dev.comment }}</button> |
|||
<input type="text" class="form-control dropdown-toggle selectajax-inp hidden" data-toggle="dropdown" |
|||
id="id_parent_dev" placeholder="{% trans 'Find the device' %}"> |
|||
{% else %} |
|||
<button class="selectajax-btn form-control btn btn-default hidden"></button> |
|||
<input type="text" class="form-control dropdown-toggle selectajax-inp" data-toggle="dropdown" |
|||
id="id_parent_dev" placeholder="{% trans 'Find the device' %}"> |
|||
{% endif %} |
|||
<ul class="dropdown-menu selectajax-ul"></ul> |
|||
{{ form.parent_dev.errors }} |
|||
{% block main %} |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"> |
|||
<h3 class="panel-title">{% trans 'Fix device group' %}</h3> |
|||
</div> |
|||
<div class="panel-body"> |
|||
|
|||
<form role="form" action="{% url 'devapp:fix_device_group' dev.pk %}" method="post">{% csrf_token %} |
|||
|
|||
{% bootstrap_icon 'globe' as ic %} |
|||
{% bootstrap_field form.ip_address addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'globe' as ic %} |
|||
{% bootstrap_field form.mac_addr addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'comment' as ic %} |
|||
{% bootstrap_field form.comment addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'hdd' as ic %} |
|||
{% bootstrap_field form.devtype addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'lock' as ic %} |
|||
{% bootstrap_field form.man_passw addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'subscript' as ic %} |
|||
{% bootstrap_field form.group addon_before=ic %} |
|||
|
|||
<div class="form-group"> |
|||
<label for="id_parent_dev">{% trans 'Parent device' %}</label> |
|||
|
|||
<div class="input-group selectajax" data-dst="/dev/search_dev"> |
|||
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span> |
|||
<input type="hidden" name="parent_dev" class="selectajax-hid" {% if selected_parent_dev %} |
|||
value="{{ selected_parent_dev.pk }}" {% endif %}> |
|||
{% if selected_parent_dev %} |
|||
<button class="selectajax-btn form-control btn btn-default">{{ selected_parent_dev.comment }}</button> |
|||
<input type="text" class="form-control dropdown-toggle selectajax-inp hidden" data-toggle="dropdown" |
|||
id="id_parent_dev" placeholder="{% trans 'Find the device' %}"> |
|||
{% else %} |
|||
<button class="selectajax-btn form-control btn btn-default hidden"></button> |
|||
<input type="text" class="form-control dropdown-toggle selectajax-inp" data-toggle="dropdown" |
|||
id="id_parent_dev" placeholder="{% trans 'Find the device' %}"> |
|||
{% endif %} |
|||
<ul class="dropdown-menu selectajax-ul"></ul> |
|||
{{ form.parent_dev.errors }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="btn-group btn-group-sm"> |
|||
<button type="submit" class="btn btn-primary"> |
|||
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %} |
|||
</button> |
|||
<button type="reset" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %} |
|||
</button> |
|||
</div> |
|||
<div class="btn-group btn-group-sm"> |
|||
<button type="submit" class="btn btn-primary"> |
|||
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %} |
|||
</button> |
|||
<button type="reset" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %} |
|||
</button> |
|||
</div> |
|||
|
|||
</form> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -1,23 +1,25 @@ |
|||
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
|||
{% load i18n %} |
|||
{% block main %} |
|||
|
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'group_app:group_list' %}">{% trans 'Groups' %}</a></li> |
|||
<li class="active">{% block active_text %}{% trans 'Groups' %}{% endblock %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
{% include 'message_block.html' %} |
|||
|
|||
<div class="page-header"> |
|||
<h2>{% trans 'Groups' %}</h2> |
|||
</div> |
|||
{% block page-header %} |
|||
<h3>{% trans 'Groups' %}</h3> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block main %} |
|||
<div class="panel panel-default"> |
|||
<div class="panel-body"> |
|||
{% block content %}{% endblock %} |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -0,0 +1,5 @@ |
|||
from django.contrib import admin |
|||
from ip_pool import models |
|||
|
|||
admin.site.register(models.NetworkModel) |
|||
admin.site.register(models.EmployedIpModel) |
|||
@ -0,0 +1,5 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class IpPoolConfig(AppConfig): |
|||
name = 'ip_pool' |
|||
@ -0,0 +1,30 @@ |
|||
from netaddr import IPNetwork, AddrFormatError, IPAddress |
|||
from django import forms |
|||
from django.core.exceptions import ValidationError |
|||
from ip_pool import models |
|||
|
|||
|
|||
class NetworkForm(forms.ModelForm): |
|||
|
|||
def clean_network(self): |
|||
network = self.cleaned_data.get('network') |
|||
try: |
|||
return IPAddress(network) |
|||
except AddrFormatError as e: |
|||
raise ValidationError(e, code='invalid') |
|||
|
|||
class Meta: |
|||
model = models.NetworkModel |
|||
fields = '__all__' |
|||
|
|||
|
|||
class EmployedIpForm(forms.ModelForm): |
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
super().__init__(*args, **kwargs) |
|||
if self.instance is None: |
|||
self.fields['ip'].initial = '127.0.0.1' |
|||
|
|||
class Meta: |
|||
model = models.EmployedIpModel |
|||
fields = '__all__' |
|||
@ -0,0 +1,56 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.11 on 2018-06-12 13:24 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='EmployedIpModel', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('ip', models.GenericIPAddressField(unique=True, verbose_name='Ip address')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Employed ip', |
|||
'verbose_name_plural': 'Employed ip addresses', |
|||
'db_table': 'ip_pool_employed_ip', |
|||
'ordering': ('-id',), |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='NetworkModel', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('network', models.GenericIPAddressField(help_text='Dot separated ip address. For example: 192.168.1.100', unique=True, verbose_name='IP network')), |
|||
('mask', models.PositiveSmallIntegerField(default=24, help_text='For example: 24, if network is 192.168.1.0/24', verbose_name='Mask')), |
|||
('work_range_start_ip', models.GenericIPAddressField(default=2, help_text='Ip from 192.168.1.2 may be used', verbose_name='Work range start ip')), |
|||
('work_range_end_ip', models.GenericIPAddressField(default=254, help_text='Ip may be used until 192.168.1.254', verbose_name='Work range end ip')), |
|||
('description', models.CharField(max_length=64, verbose_name='Description')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Network', |
|||
'verbose_name_plural': 'Networks', |
|||
'db_table': 'ip_pool_network', |
|||
'ordering': ('description',), |
|||
}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='employedipmodel', |
|||
name='network', |
|||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ip_pool.NetworkModel', verbose_name='Parent network'), |
|||
), |
|||
migrations.AlterUniqueTogether( |
|||
name='employedipmodel', |
|||
unique_together=set([('ip', 'network')]), |
|||
), |
|||
] |
|||
@ -0,0 +1,91 @@ |
|||
from typing import Optional |
|||
|
|||
from django.shortcuts import resolve_url |
|||
from netaddr import IPNetwork, IPAddress |
|||
from django.core.exceptions import ValidationError |
|||
from django.db import models |
|||
from django.utils.translation import gettext_lazy as _ |
|||
|
|||
|
|||
class NetworkModel(models.Model): |
|||
_netw_cache = None |
|||
|
|||
network = models.GenericIPAddressField( |
|||
verbose_name=_('IP network'), |
|||
help_text=_('Dot separated ip address of network. For example: 192.168.1.0'), |
|||
unique=True |
|||
) |
|||
mask = models.PositiveSmallIntegerField( |
|||
_('Mask'), |
|||
help_text=_('For example: 24, if network is 192.168.1.0/24'), |
|||
default=24 |
|||
) |
|||
work_range_start_ip = models.GenericIPAddressField( |
|||
verbose_name=_('Work range start ip'), |
|||
help_text=_('For example 192.168.1.2, this is first ip that may be used') |
|||
) |
|||
work_range_end_ip = models.GenericIPAddressField( |
|||
verbose_name=_('Work range end ip'), |
|||
help_text=_('Ip may be used until 192.168.1.254') |
|||
) |
|||
description = models.CharField(_('Description'), max_length=64) |
|||
|
|||
def __str__(self): |
|||
return "%s: %s/%d" % (self.description, self.network, self.mask) |
|||
|
|||
def get_network(self) -> IPNetwork: |
|||
if self._netw_cache is None: |
|||
self._netw_cache = IPNetwork("%s/%s" % (self.network, self.mask or 32)) |
|||
return self._netw_cache |
|||
|
|||
def get_absolute_url(self): |
|||
return resolve_url('ip_pool:net_edit', self.pk) |
|||
|
|||
class Meta: |
|||
db_table = 'ip_pool_network' |
|||
verbose_name = _('Network') |
|||
verbose_name_plural = _('Networks') |
|||
ordering = ('description',) |
|||
|
|||
|
|||
class EmployedIpManager(models.Manager): |
|||
|
|||
def get_free_ip(self, network: NetworkModel) -> Optional[IPAddress]: |
|||
netw = IPNetwork(network) |
|||
employed_ip_queryset = self.filter(network=network) |
|||
free_ip = next(IPAddress(net) for ip, net in zip( |
|||
employed_ip_queryset, netw |
|||
) if ip != net) |
|||
return free_ip |
|||
|
|||
|
|||
class EmployedIpModel(models.Model): |
|||
ip = models.GenericIPAddressField(verbose_name=_('Ip address'), unique=True) |
|||
network = models.ForeignKey(NetworkModel, on_delete=models.CASCADE, verbose_name=_('Parent network')) |
|||
|
|||
objects = EmployedIpManager() |
|||
|
|||
def __str__(self): |
|||
return self.ip |
|||
|
|||
def clean(self): |
|||
ip = IPAddress(self.ip) |
|||
network = self.network.get_network() |
|||
|
|||
if ip not in network: |
|||
raise ValidationError(_('Ip address %(ip)s not in %(net)s network') % { |
|||
'ip': ip, |
|||
'net': network |
|||
}, code='invalid') |
|||
|
|||
start_allowed_ip = IPAddress(self.network.work_range_start_ip) |
|||
end_allowed_ip = IPAddress(self.network.work_range_end_ip) |
|||
if not start_allowed_ip <= ip <= end_allowed_ip: |
|||
raise ValidationError(_('Ip address that you entered is not in work range'), code='invalid') |
|||
|
|||
class Meta: |
|||
db_table = 'ip_pool_employed_ip' |
|||
verbose_name = _('Employed ip') |
|||
verbose_name_plural = _('Employed ip addresses') |
|||
ordering = ('-id',) |
|||
unique_together = ('ip', 'network') |
|||
@ -0,0 +1,39 @@ |
|||
{% extends 'base.html' %} |
|||
{% load i18n %} |
|||
{% load bootstrap3 %} |
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'ip_pool:networks' %}">{% trans 'Ip pool' %}</a></li> |
|||
<li><a href="{% url 'ip_pool:net_edit' net.id %}">{{ net }}</a></li> |
|||
<li class="active">{% trans 'Ip list' %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
{% block page-header %}{% trans 'Ip list' %}{% endblock %} |
|||
|
|||
{% block main %} |
|||
<div class="table-responsive"> |
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th class="col-sm-5">{% trans 'Ip' %}</th> |
|||
<th class="col-sm-3">{% trans 'Network' %}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for ip in object_list %} |
|||
<tr> |
|||
<td>{{ ip.ip }}</td> |
|||
<td>{{ ip.network }}</td> |
|||
</tr> |
|||
{% empty %} |
|||
<tr> |
|||
<td colspan="2">{% trans 'You have not any available dedicated ips in this network' %}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
{% endblock %} |
|||
@ -0,0 +1,30 @@ |
|||
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
|||
{% load i18n %} |
|||
|
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li class="active">{% trans 'Ip pool' %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block main %} |
|||
|
|||
<ul class="nav nav-tabs"> |
|||
{% url 'taskapp:home' as taskhome %} |
|||
<li{% if taskhome == request.path %} class="active"{% endif %}> |
|||
<a href="{{ taskhome }}"> |
|||
{% trans 'New tasks' %} |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
<div class="tab-content"> |
|||
<div class="tab-pane active"> |
|||
{% block content %}{% endblock %} |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -0,0 +1,35 @@ |
|||
{% extends 'base.html' %} |
|||
{% load i18n %} |
|||
{% load bootstrap3 %} |
|||
{% load globaltags %} |
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'ip_pool:networks' %}">{% trans 'Ip pool' %}</a></li> |
|||
<li class="active">{% trans 'Add network' %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
{% block main %} |
|||
<form action="{% url 'ip_pool:net_add' %}" method="post">{% csrf_token %} |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"> |
|||
<h3 class="panel-title">{% trans 'Add network' %}</h3> |
|||
</div> |
|||
<div class="panel-body"> |
|||
{% bootstrap_form form %} |
|||
</div> |
|||
<div class="panel-footer"> |
|||
<div class="btn-group"> |
|||
<button class="btn btn-success"> |
|||
<span class="glyphicon glyphicon-save"></span> {% trans 'Add' %} |
|||
</button> |
|||
<a href="{% back_url request %}" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-backward"></span> {% trans 'Back' %} |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
{% endblock %} |
|||
@ -0,0 +1,41 @@ |
|||
{% extends 'base.html' %} |
|||
{% load i18n %} |
|||
{% load bootstrap3 %} |
|||
{% load globaltags %} |
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li><a href="{% url 'ip_pool:networks' %}">{% trans 'Ip pool' %}</a></li> |
|||
<li class="active">{{ object }}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
{% block page-header %}{{ object }}{% endblock %} |
|||
|
|||
{% block main %} |
|||
<form action="{% url 'ip_pool:net_edit' object.pk %}" method="post">{% csrf_token %} |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"> |
|||
<h3 class="panel-title">{% trans 'Edit network' %}</h3> |
|||
</div> |
|||
<div class="panel-body"> |
|||
{% bootstrap_form form %} |
|||
</div> |
|||
<div class="panel-footer"> |
|||
<div class="btn-group"> |
|||
<button class="btn btn-primary"> |
|||
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %} |
|||
</button> |
|||
<a href="{% back_url request %}" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-backward"></span> {% trans 'Back' %} |
|||
</a> |
|||
<a href="{% url 'ip_pool:ip_list' object.pk %}" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-eye-open"></span> |
|||
<span class="hidden-xs hidden-sm">{% trans 'View employed' %}</span> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
{% endblock %} |
|||
@ -0,0 +1,70 @@ |
|||
{% extends 'base.html' %} |
|||
{% load i18n %} |
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li class="active">{% trans 'Ip pool' %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
{% block page-header %} |
|||
{% trans 'Networks' %} |
|||
{% endblock %} |
|||
|
|||
{% block main %} |
|||
<div class="table-responsive"> |
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th class="col-sm-5">{% trans 'Network' %}</th> |
|||
<th class="col-sm-3">{% trans 'Work range start ip' %}</th> |
|||
<th class="col-sm-3">{% trans 'Work range end ip' %}</th> |
|||
<th class="col-sm-1"></th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% with can_ch_net=perms.ip_pool.change_networkmodel %} |
|||
{% for net in networks %} |
|||
<tr> |
|||
<td><a href="{% url 'ip_pool:ip_list' net.pk %}">{{ net }}</a></td> |
|||
<td>{{ net.work_range_start_ip }}</td> |
|||
<td>{{ net.work_range_end_ip }}</td> |
|||
<td class="btn-group btn-group-sm btn-group-justified"> |
|||
{% if can_ch_net %} |
|||
<a href="{% url 'ip_pool:net_edit' net.pk %}" class="btn btn-primary"> |
|||
<span class="glyphicon glyphicon-edit"></span> |
|||
<span class="hidden-xs hidden-sm">{% trans 'Edit' %}</span> |
|||
</a> |
|||
{% else %} |
|||
<a href="#" class="btn btn-primary disabled"> |
|||
<span class="glyphicon glyphicon-edit"></span> |
|||
<span class="hidden-xs hidden-sm">{% trans 'Edit' %}</span> |
|||
</a> |
|||
{% endif %} |
|||
<a href="{% url 'ip_pool:ip_list' net.pk %}" class="btn btn-default"> |
|||
<span class="glyphicon glyphicon-eye-open"></span> |
|||
<span class="hidden-xs hidden-sm">{% trans 'View employed' %}</span> |
|||
</a> |
|||
</td> |
|||
</tr> |
|||
{% empty %} |
|||
<tr> |
|||
<td colspan="4">{% trans 'You have not any networks available' %}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
{% endwith %} |
|||
</tbody> |
|||
<tfoot> |
|||
<tr> |
|||
<td colspan="4"> |
|||
<a href="{% url 'ip_pool:net_add' %}" class="btn btn-success"> |
|||
<span class="glyphicon glyphicon-plus"></span> |
|||
{% trans 'Add' %} |
|||
</a> |
|||
</td> |
|||
</tr> |
|||
</tfoot> |
|||
</table> |
|||
</div> |
|||
{% endblock %} |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,11 @@ |
|||
from django.conf.urls import url |
|||
from ip_pool import views |
|||
|
|||
app_name = 'ip_pool' |
|||
|
|||
urlpatterns = [ |
|||
url('^$', views.NetworksListView.as_view(), name='networks'), |
|||
url('^network_add/$', views.NetworkCreateView.as_view(), name='net_add'), |
|||
url('^(?P<net_id>\d{1,6})/$', views.IpEmployedListView.as_view(), name='ip_list'), |
|||
url('^(?P<net_id>\d{1,6})/edit$', views.NetworkUpdateView.as_view(), name='net_edit'), |
|||
] |
|||
@ -0,0 +1,60 @@ |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.contrib import messages |
|||
from django.shortcuts import resolve_url, get_object_or_404 |
|||
from django.utils.decorators import method_decorator |
|||
from django.utils.translation import gettext_lazy as _ |
|||
from django.views.generic import UpdateView, CreateView |
|||
|
|||
from guardian.decorators import permission_required_or_403 as permission_required |
|||
from djing.global_base_views import BaseOrderedFilteringList |
|||
from ip_pool import models, forms |
|||
|
|||
|
|||
@method_decorator(login_required, name='dispatch') |
|||
class NetworksListView(BaseOrderedFilteringList): |
|||
template_name = 'ip_pool/network_list.html' |
|||
context_object_name = 'networks' |
|||
model = models.NetworkModel |
|||
|
|||
|
|||
@method_decorator(login_required, name='dispatch') |
|||
@method_decorator(permission_required('ip_pool.change_networkmodel'), name='dispatch') |
|||
class NetworkUpdateView(UpdateView): |
|||
model = models.NetworkModel |
|||
template_name = 'ip_pool/net_edit.html' |
|||
form_class = forms.NetworkForm |
|||
pk_url_kwarg = 'net_id' |
|||
|
|||
def form_valid(self, form): |
|||
r = super().form_valid(form) |
|||
messages.success(self.request, _('Network successfully updated')) |
|||
return r |
|||
|
|||
|
|||
@method_decorator(login_required, name='dispatch') |
|||
class IpEmployedListView(BaseOrderedFilteringList): |
|||
template_name = 'ip_pool/employed_ip_list.html' |
|||
model = models.EmployedIpModel |
|||
|
|||
def get_context_data(self, **kwargs): |
|||
net_id = self.kwargs.get('net_id') |
|||
context = super().get_context_data(**kwargs) |
|||
context['net'] = get_object_or_404(models.NetworkModel, pk=net_id) |
|||
return context |
|||
|
|||
def get_queryset(self): |
|||
net_id = self.kwargs.get('net_id') |
|||
return self.model.objects.filter(network__id=net_id) |
|||
|
|||
|
|||
@method_decorator(login_required, name='dispatch') |
|||
@method_decorator(permission_required('ip_pool.add_networkmodel'), name='dispatch') |
|||
class NetworkCreateView(CreateView): |
|||
model = models.NetworkModel |
|||
template_name = 'ip_pool/net_add.html' |
|||
form_class = forms.NetworkForm |
|||
|
|||
def form_valid(self, form): |
|||
r = super().form_valid(form) |
|||
messages.success(self.request, _('Network has been created')) |
|||
return r |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue