diff --git a/abonapp/models.py b/abonapp/models.py index cdf3b86..9b4978f 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -7,7 +7,7 @@ from django.conf import settings from django.core import validators from django.core.validators import RegexValidator from django.db import models, connection, transaction -from django.db.models.signals import post_delete, pre_delete, post_init, \ +from django.db.models.signals import pre_delete, post_init, \ pre_save from django.dispatch import receiver from django.shortcuts import resolve_url @@ -348,24 +348,6 @@ class Abon(BaseAccount): except LogicError: pass - def nas_remove_self(self): - """ - Will remove this user to network access server - :return: - """ - if self.nas is None: - raise LogicError(_('gateway required')) - try: - agent_abon = self.build_agent_struct() - if agent_abon is not None: - mngr = self.nas.get_nas_manager() - mngr.remove_user(agent_abon) - except (NasFailedResult, NasNetworkError, ConnectionResetError) as e: - print('ERROR:', e) - return e - except LogicError: - pass - def get_absolute_url(self): return resolve_url('abonapp:abon_home', self.group.id, self.username) @@ -608,17 +590,6 @@ class PeriodicPayForId(models.Model): ordering = ('last_pay',) -@receiver(post_delete, sender=Abon) -def abon_del_signal(sender, **kwargs): - abon = kwargs.get("instance") - if abon is None: - raise ValueError('Instance does not passed to a signal') - try: - abon.nas_remove_self() - except (NasFailedResult, NasNetworkError, LogicError): - return True - - @receiver(post_init, sender=AbonTariff) def abon_tariff_post_init(sender, **kwargs): abon_tariff = kwargs["instance"] @@ -635,17 +606,3 @@ def abon_tariff_pre_save(sender, **kwargs): if getattr(abon_tariff, 'deadline') is None: calc_obj = abon_tariff.tariff.get_calc_type()(abon_tariff) abon_tariff.deadline = calc_obj.calc_deadline() - - -@receiver(pre_delete, sender=AbonTariff) -def abontariff_pre_delete(sender, **kwargs): - abon_tariff = kwargs.get("instance") - if abon_tariff is None: - raise ValueError('Instance does not passed to a signal') - try: - abon = Abon.objects.get(current_tariff=abon_tariff) - abon.nas_remove_self() - except (NasFailedResult, NasNetworkError, LogicError): - return True - except Abon.DoesNotExist: - print('Error: abontariff_pre_delete - user not found') diff --git a/abonapp/tasks.py b/abonapp/tasks.py new file mode 100644 index 0000000..22e8773 --- /dev/null +++ b/abonapp/tasks.py @@ -0,0 +1,47 @@ +from celery import shared_task + +from abonapp.models import Abon +from djing.lib import LogicError +from gw_app.models import NASModel +from gw_app.nas_managers import NasFailedResult, NasNetworkError, SubnetQueue + + +@shared_task +def customer_nas_command(customer_uid: int, command: str): + if command not in ('add', 'sync'): + return 'Command required' + try: + cust = Abon.objects.get(pk=customer_uid) + print(cust, command) + if command == 'sync': + r = cust.nas_sync_self() + if isinstance(r, Exception): + return 'ABONAPP SYNC ERROR: %s' % r + elif command == 'add': + cust.nas_add_self() + else: + return 'ABONAPP SYNC ERROR: Unknown command "%s"' % command + except Abon.DoesNotExist: + pass + except (LogicError, NasFailedResult, NasNetworkError, ConnectionResetError) as e: + return 'ABONAPP ERROR: %s' % e + + +@shared_task +def customer_nas_remove(customer_uid: int, ip_addr: str, speed: tuple, is_access: bool, nas_pk: int): + try: + if not isinstance(ip_addr, (str, int)): + ip_addr = str(ip_addr) + sq = SubnetQueue( + name="uid%d" % customer_uid, + network=ip_addr, + max_limit=speed, + is_access=is_access + ) + nas = NASModel.objects.get(pk=nas_pk) + mngr = nas.get_nas_manager() + mngr.remove_user(sq) + except (ValueError, NasFailedResult, NasNetworkError, LogicError) as e: + return 'ABONAPP ERROR: %s' % e + except NASModel.DoesNotExist: + return 'NASModel.DoesNotExist id=%d' % nas_pk diff --git a/abonapp/views.py b/abonapp/views.py index f68b4c5..f0075b2 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import Dict, Optional +from abonapp.tasks import customer_nas_command, customer_nas_remove from agent.commands.dhcp import dhcp_commit, dhcp_expiry, dhcp_release from devapp.models import Device, Port as DevPort from dialing_app.models import AsteriskCDR @@ -172,6 +173,13 @@ class DelAbonDeleteView(LoginAdminMixin, PermissionRequiredMixin, DeleteView): try: abon = self.get_object() gid = abon.group.id + if abon.current_tariff: + abon_tariff = abon.current_tariff.tariff + customer_nas_remove.delay( + customer_uid=abon.pk, ip_addr=abon.ip_address, + speed=(abon_tariff.speedIn, abon_tariff.speedOut), + is_access=abon.is_access(), nas_pk=abon.nas_id + ) abon.delete() request.user.log(request.META, 'dusr', ( '%(uname)s, "%(fio)s", %(group)s %(street)s %(house)s' % { @@ -336,9 +344,7 @@ class AbonHomeUpdateView(LoginAdminMixin, PermissionRequiredMixin, UpdateView): def form_valid(self, form): r = super(AbonHomeUpdateView, self).form_valid(form) abon = self.object - res = abon.nas_sync_self() - if isinstance(res, Exception): - messages.warning(self.request, res) + customer_nas_command.delay(abon.pk, 'sync') messages.success(self.request, _('edit abon success msg')) return r @@ -450,11 +456,8 @@ def pick_tariff(request, gid: int, uname): comment=log_comment) else: abon.pick_tariff(trf, request.user, comment=log_comment) - r = abon.nas_sync_self() - if r is None: - messages.success(request, _('Tariff has been picked')) - else: - messages.error(request, r) + customer_nas_command.delay(abon.pk, 'sync') + messages.success(request, _('Tariff has been picked')) return redirect('abonapp:abon_services', gid=gid, uname=abon.username) except (lib.LogicError, NasFailedResult) as e: @@ -489,6 +492,13 @@ def unsubscribe_service(request, gid: int, uname, abon_tariff_id: int): try: abon_tariff = get_object_or_404(models.AbonTariff, pk=int(abon_tariff_id)) + abon = abon_tariff.abon + trf = abon_tariff.tariff + customer_nas_remove.delay( + customer_uid=abon.pk, ip_addr=abon.ip_address, + speed=(trf.speedIn, trf.speedOut), + is_access=abon.is_access(), nas_pk=abon.nas_id + ) abon_tariff.delete() messages.success(request, _('User has been detached from service')) except NasFailedResult as e: @@ -610,9 +620,7 @@ class IpUpdateView(LoginAdminPermissionMixin, UpdateView): def form_valid(self, form): r = super(IpUpdateView, self).form_valid(form) abon = self.object - res = abon.nas_sync_self() - if isinstance(res, Exception): - messages.warning(self.request, res) + customer_nas_command.delay(abon.pk, 'sync') messages.success(self.request, _('Ip successfully updated')) return r @@ -1219,6 +1227,7 @@ def user_session_free(request, gid: int, uname): return redirect('abonapp:abon_home', gid, uname) if abon.ip_address: abon.free_ip_addr() + customer_nas_command.delay(abon.pk, 'remove') messages.success(request, _('Ip lease has been freed')) else: messages.error(request, _('User not have ip')) diff --git a/clientsideapp/views.py b/clientsideapp/views.py index 0a5c166..81a5ade 100644 --- a/clientsideapp/views.py +++ b/clientsideapp/views.py @@ -5,6 +5,7 @@ from django.db import transaction from django.utils.translation import gettext_lazy as _, gettext from abonapp.models import AbonLog, InvoiceForPayment, Abon +from abonapp.tasks import customer_nas_command from djing.lib.decorators import json_view from tariff_app.models import Tariff from taskapp.models import Task @@ -52,7 +53,7 @@ def buy_service(request, srv_id): if request.method == 'POST': abon.pick_tariff(service, None, _("Buy the service via user side, service '%s'") % service) - abon.nas_sync_self() + customer_nas_command.delay(abon.pk, 'sync') messages.success(request, _("The service '%s' wan successfully activated") % service.title) else: return render(request, 'clientsideapp/modal_service_buy.html', { diff --git a/gw_app/nas_managers/structs.py b/gw_app/nas_managers/structs.py index 7cd8230..d880808 100644 --- a/gw_app/nas_managers/structs.py +++ b/gw_app/nas_managers/structs.py @@ -23,7 +23,7 @@ class SubnetQueue(BaseStruct): return self._max_limit def set_max_limit(self, v): - if isinstance(v, tuple): + if isinstance(v, (tuple, list)): self._max_limit = v elif isinstance(v, str): s_in, s_out = v.split('/') @@ -32,7 +32,7 @@ class SubnetQueue(BaseStruct): sp = float(v) self._max_limit = sp, sp else: - raise ValueError('Unexpected format for max_limit') + raise ValueError('Unexpected format for max_limit %s' % v) max_limit = property(get_max_limit, set_max_limit)