diff --git a/abonapp/models.py b/abonapp/models.py index 806ada4..ee22b0a 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -16,7 +16,7 @@ from accounts_app.models import UserProfile, MyUserManager, BaseAccount from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError from group_app.models import Group from djing.lib import LogicError -from ip_pool.models import IpLeaseModel +from ip_pool.models import IpLeaseModel, NetworkModel from tariff_app.models import Tariff, PeriodicPay from bitfield import BitField @@ -225,18 +225,12 @@ class Abon(BaseAccount): agent_trf = TariffStruct(trf.id, trf.speedIn, trf.speedOut) return AbonStruct(self.pk, abon_addresses, agent_trf, self.is_access()) - # def clean(self): - # # check if ip address already busy - # abon_addresses = tuple(p.ip for p in self.ip_addresses.all().iterator()) - # if self.ip_address is not None and Abon.objects.filter(ip_address=self.ip_address).exclude( - # pk=self.pk).count() > 0: - # raise ValidationError({'ip_address': (gettext('Ip address already exist'),)}) - # return super(Abon, self).clean() - def sync_with_nas(self, created: bool) -> Optional[Exception]: agent_abon = self.build_agent_struct() if agent_abon is None: return + if len(agent_abon.ips) < 1: + return _('Account has no one ips') try: tm = Transmitter() if created: @@ -250,14 +244,13 @@ 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, mac_addr=None): + def add_lease(self, ip: str, network: Optional[NetworkModel], mac_addr=None): 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, net=None, mac=mac_addr) + lease = IpLeaseModel.objects.create_from_ip(ip=ip, net=network, mac=mac_addr) if lease is None: - return 'Subnet not found' + return 'Error while creating a ip lease' self.ip_addresses.add(lease) - #self.save() class PassportInfo(models.Model): diff --git a/abonapp/templates/abonapp/editAbon.html b/abonapp/templates/abonapp/editAbon.html index f64c739..04ff226 100644 --- a/abonapp/templates/abonapp/editAbon.html +++ b/abonapp/templates/abonapp/editAbon.html @@ -215,7 +215,11 @@ {% endif %} {{ lease }} - {% trans 'Leased by:' %} {{ lease.lease_time|date:'d-m H:i:s' }} + {% trans 'Leased by:' %} {{ lease.lease_time|date:'d-m H:i:s' }}. + {% if lease.mac_addr %} + {% trans 'From' %}: {{ lease.mac_addr }}. + {% endif %} + {% empty %} diff --git a/abonapp/views.py b/abonapp/views.py index 5d692e9..a6874aa 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -421,8 +421,11 @@ def pick_tariff(request, gid, uname): else: deadline = datetime.strptime(deadline, '%Y-%m-%d %H:%M:%S') abon.pick_tariff(trf, request.user, deadline=deadline, comment=log_comment) - abon.sync_with_nas(created=False) - messages.success(request, _('Tariff has been picked')) + r = abon.sync_with_nas(created=False) + if r is None: + messages.success(request, _('Tariff has been picked')) + else: + messages.error(request, r) return redirect('abonapp:abon_services', gid=gid, uname=abon.username) except (lib.LogicError, NasFailedResult) as e: messages.error(request, e) @@ -554,7 +557,6 @@ def chgroup_tariff(request, gid): tr = request.POST.getlist('tr') grp.tariff_set.clear() grp.tariff_set.add(*tr) - grp.save() messages.success(request, _('Successfully saved')) return redirect('abonapp:ch_group_tariff', gid) tariffs = Tariff.objects.all() @@ -1001,20 +1003,6 @@ def abon_export(request, gid): }, request=request) -# @login_required -# @permission_required('abonapp.change_abon') -# @permission_required('group_app.can_view_group', (Group, 'pk', 'gid')) -# @json_view -# def reset_ip(request, gid, uname): -# abon = get_object_or_404(models.Abon, username=uname) -# abon.ip_address = None -# abon.save(update_fields=('ip_address',)) -# return { -# 'status': 0, -# 'dat': "" -# } - - @login_required @lib.decorators.only_admins def fin_report(request): @@ -1151,6 +1139,11 @@ def lease_add(request, gid, uname): 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) + tm = Transmitter() + tm.lease_start( + user=abon.build_agent_struct(), + lease=lease.ip + ) messages.success(request, _('Ip lease has been created')) return redirect('abonapp:abon_home', gid, uname) except lib.DuplicateEntry as e: @@ -1233,19 +1226,24 @@ class DhcpLever(SecureApiView): 'switch_port': 3, 'cmd': 'commit' }""" - r = None try: - action = data['cmd'] + action = data.get('cmd') + if action is None: + return '"cmd" parameter is missing' + client_ip = data.get('client_ip') + if client_ip is None: + return '"client_ip" parameter is missing' if action == 'commit': - r = dhcp_commit( - data['client_ip'], data['client_mac'], - data['switch_mac'], data['switch_port'] + return dhcp_commit( + client_ip, data.get('client_mac'), + data.get('switch_mac'), data.get('switch_port') ) elif action == 'expiry': - r = dhcp_expiry(data['client_ip']) + return dhcp_expiry(client_ip) elif action == 'release': - r = dhcp_release(data['client_ip']) + return dhcp_release(client_ip) + else: + return '"cmd" parameter is invalid: %s' % action except lib.LogicError as e: print('LogicError', e) - r = str(e) - return r + return str(e) diff --git a/agent/commands/dhcp.py b/agent/commands/dhcp.py index 73fd293..1bef7e3 100644 --- a/agent/commands/dhcp.py +++ b/agent/commands/dhcp.py @@ -18,7 +18,10 @@ 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' - add_lease_result = abon.add_lease(client_ip) + client_ips = tuple(str(ip) for ip in abon.ip_addresses.all()) + if client_ip in client_ips: + return 'Ip address already existed' + add_lease_result = abon.add_lease(client_ip, mac_addr=client_mac, network=None) if add_lease_result is None: if abon.is_access(): abon.sync_with_nas(created=False) diff --git a/agent/core.py b/agent/core.py index b9194e6..130420c 100644 --- a/agent/core.py +++ b/agent/core.py @@ -108,12 +108,12 @@ class BaseTransmitter(ABC): def sync_nas(self, users_from_db: Iterator): list_for_add, list_for_del = self._diff_users(users_from_db) if len(list_for_del) > 0: - print('List for del:') + print('List for del:', len(list_for_del)) for ld in list_for_del: - print('\t', ld.ips) + print('\t', ld) self.remove_user_range(list_for_del) if len(list_for_add) > 0: - print('List for add:') + print('List for add:', len(list_for_add)) for la in list_for_add: - print('\t', la.ips) + print('\t', la) self.add_user_range(list_for_add) diff --git a/agent/mod_mikrotik.py b/agent/mod_mikrotik.py index 9a44a27..2cdba3f 100644 --- a/agent/mod_mikrotik.py +++ b/agent/mod_mikrotik.py @@ -490,6 +490,8 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs raise NasFailedResult(_('You cannot disable last session')) def lease_start(self, user: AbonStruct, lease): + if not issubclass(lease.__class__, _BaseAddress): + lease = ip_address(lease) ip = self.find_ip(lease, LIST_USERS_ALLOWED) if ip is None: self.add_ip(LIST_USERS_ALLOWED, lease) diff --git a/agent/structs.py b/agent/structs.py index 2bc187e..3a603b9 100644 --- a/agent/structs.py +++ b/agent/structs.py @@ -36,30 +36,38 @@ class TariffStruct(BaseStruct): # Abon from database class AbonStruct(BaseStruct): - __slots__ = ('uid', 'ips', 'tariff', 'is_access', 'queue_id') + __slots__ = ('uid', '_ips', 'tariff', 'is_access', 'queue_id') def __init__(self, uid=0, ips=None, tariff=None, is_access=True): self.uid = int(uid or 0) if ips is None: - self.ips = () + self._ips = () else: - self.ips = tuple(ip_address(ip) for ip in ips) + self._ips = tuple(ip_address(ip) for ip in ips) self.tariff = tariff self.is_access = is_access self.queue_id = 0 + def get_ips(self): + return self._ips + + def set_ips(self, v): + self._ips = set(v) + + ips = property(get_ips, set_ips, doc='Ip addresses') + def __eq__(self, other): if not isinstance(other, AbonStruct): raise TypeError - r = self.uid == other.uid and self.ips == other.ips + r = self.uid == other.uid and self._ips == other._ips r = r and self.tariff == other.tariff return r def __str__(self): - return "uid=%d, ips=[%s], tariff=%s" % (self.uid, ';'.join(self.ips), self.tariff or '') + return "uid=%d, ips=[%s], tariff=%s" % (self.uid, ';'.join(str(i) for i in self._ips), self.tariff or '') def __hash__(self): - return hash(hash(self.ips) + hash(self.tariff)) if self.tariff is not None else 0 + return hash(hash(self._ips) + hash(self.tariff)) if self.tariff is not None else 0 # Shape rule from NAS(Network Access Server) diff --git a/devapp/migrations/0003_auto_20180529_1311.py b/devapp/migrations/0003_auto_20180529_1311.py index 747c845..16baee2 100644 --- a/devapp/migrations/0003_auto_20180529_1311.py +++ b/devapp/migrations/0003_auto_20180529_1311.py @@ -14,7 +14,7 @@ def snmp_backup_info(apps, _): Device = apps.get_model('devapp', 'Device') obs = Device.objects.only('snmp_item_num') with open(TMP_FILE, 'w') as f: - serializers.serialize('json', obs, stream=f) + serializers.serialize('json', obs, stream=f, fields=('snmp_item_num',)) def snmp_restore_info_to_new_scheme(apps, _): diff --git a/devapp/views.py b/devapp/views.py index deb8289..353b4fc 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -123,8 +123,11 @@ class DeviceUpdate(UpdateView): pass r = super().form_valid(form) # change device info in dhcpd.conf - self.object.update_dhcp() - messages.success(self.request, _('Device info has been saved')) + try: + self.object.update_dhcp() + messages.success(self.request, _('Device info has been saved')) + except PermissionError as e: + messages.error(self.request, e) return r def dispatch(self, request, *args, **kwargs): @@ -177,8 +180,11 @@ class DeviceCreateView(CreateView): pass r = super().form_valid(form) # change device info in dhcpd.conf - self.object.update_dhcp() - messages.success(self.request, _('Device info has been saved')) + try: + self.object.update_dhcp() + messages.success(self.request, _('Device info has been saved')) + except PermissionError as e: + messages.error(self.request, e) return r def dispatch(self, request, *args, **kwargs): diff --git a/ip_pool/migrations/0001_initial.py b/ip_pool/migrations/0001_initial.py index b8db851..9ce0cdb 100644 --- a/ip_pool/migrations/0001_initial.py +++ b/ip_pool/migrations/0001_initial.py @@ -56,7 +56,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='ipleasemodel', name='network', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ip_pool.NetworkModel', verbose_name='Parent network'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ip_pool.NetworkModel', verbose_name='Parent network', blank=True, null=True), ), migrations.AlterUniqueTogether( name='ipleasemodel', diff --git a/ip_pool/models.py b/ip_pool/models.py index 5d389ab..642e780 100644 --- a/ip_pool/models.py +++ b/ip_pool/models.py @@ -163,7 +163,7 @@ class IpLeaseManager(models.Manager): class IpLeaseModel(models.Model): ip = models.GenericIPAddressField(verbose_name=_('Ip address'), unique=True) - network = models.ForeignKey(NetworkModel, on_delete=models.CASCADE, verbose_name=_('Parent network')) + network = models.ForeignKey(NetworkModel, on_delete=models.CASCADE, verbose_name=_('Parent network'), null=True, blank=True) mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True) lease_time = models.DateTimeField(_('Lease time'), auto_now_add=True) is_dynamic = models.BooleanField(_('Is dynamic'), default=False) diff --git a/periodic.py b/periodic.py index 79f94f1..abab7e3 100755 --- a/periodic.py +++ b/periodic.py @@ -6,7 +6,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") django.setup() from django.utils import timezone from django.db import transaction -from django.db.models import signals +from django.db.models import signals, Count from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, PeriodicPayForId, AbonLog from ip_pool.models import IpLeaseModel from agent import Transmitter, NasNetworkError, NasFailedResult @@ -42,6 +42,8 @@ def main(): users = Abon.objects\ .filter(is_active=True)\ .exclude(current_tariff=None)\ + .annotate(ips_count=Count('ip_addresses'))\ + .filter(ips_count__gt=0)\ .prefetch_related('ip_addresses')\ .iterator() tm.sync_nas(users)