diff --git a/.gitignore b/.gitignore index 6261795..a7b0c79 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ media/min/* ~*/migrations/000*.py .idea/ djing/settings.py +*/migrations/000*.py diff --git a/abonapp/apps.py b/abonapp/apps.py new file mode 100644 index 0000000..5b70844 --- /dev/null +++ b/abonapp/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AbonappConfig(AppConfig): + name = 'abonapp' diff --git a/abonapp/forms.py b/abonapp/forms.py index 059c8bc..284bedf 100644 --- a/abonapp/forms.py +++ b/abonapp/forms.py @@ -36,7 +36,7 @@ class AbonForm(forms.Form): ) is_active = forms.BooleanField( required=False, - widget=forms.Select(attrs={'class': 'form-control', 'id': 'isactive'}) + widget=forms.NullBooleanSelect(attrs={'class': 'form-control', 'id': 'isactive'}) ) group = forms.ModelChoiceField( diff --git a/abonapp/models.py b/abonapp/models.py index c71110b..24e4a16 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -15,12 +15,17 @@ from accounts_app.models import UserProfile class LogicError(Exception): - def __init__(self, value): - self.value = value + def __init__(self, value, err_id=None): + self.value = value + if err_id: + self.err_id = err_id def __unicode__(self): return repr(self.value) + def __str__(self): + return repr(self.value) + class AbonGroup(models.Model): title = models.CharField(max_length=127) @@ -49,7 +54,8 @@ class AbonLog(models.Model): class AbonTariffManager(models.Manager): - def update_priorities(self, abonent): + @staticmethod + def update_priorities(abonent): abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority') # Обновляем приоритеты, чтоб по порядку были @@ -134,7 +140,7 @@ class AbonTariff(models.Model): next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1] if next_tarifs.count() < 1: - raise LogicError(u'У абонента нет следующих назначенных услуг') + raise LogicError('У абонента нет следующих назначенных услуг') # 0й элемент это следующая подключаемая услуга next_tarifs[0].time_start = timezone.now() @@ -177,6 +183,7 @@ class Abon(UserProfile): _act_tar_cache = None + # возвращает текущий тариф для абонента def active_tariff(self): if self._act_tar_cache: return self._act_tar_cache @@ -317,6 +324,17 @@ class Abon(UserProfile): comment = u'Завершение услуги по истечению срока действия' ) + # есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs..manage_access() + def is_access(self): + trf = self.active_tariff() + if not trf: return False + ct = trf.get_calc_type() + if ct.manage_access(self): + return True + else: + return False + + class InvoiceForPayment(models.Model): abon = models.ForeignKey(Abon) diff --git a/abonapp/views.py b/abonapp/views.py index 89f0b9e..cfa800f 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -213,6 +213,8 @@ def abonhome(request, gid, uid): try: if request.method == 'POST': + # подключение к NAS'у в начале для того чтоб если исключение то ничего не сохранялось и сразу показать ошибку + tc = get_TransmitterClientKlass()() frm = forms.AbonForm(request.POST) if frm.is_valid(): cd = frm.cleaned_data @@ -229,19 +231,22 @@ def abonhome(request, gid, uid): abon.is_active = 1 if cd['is_active'] else 0 abon.save() - # Если включили то шлём событие от этом + # Если включили абонента то шлём событие от этом if cd['is_active'] and not abisactive: - tc = get_TransmitterClientKlass()() - tc.signal_abon_enable(abon) + # смотрим есть-ли доступ у абонента к услуге + is_acc = abon.is_access() + if is_acc: + tc.signal_abon_refresh_info(abon) + else: + tc.signal_abon_close_inet(abon) - # Если выключили + + # Если выключили абонента elif not cd['is_active'] and abisactive: - tc = get_TransmitterClientKlass()() tc.signal_abon_disable(abon) # Если изменили инфу, важную для NAS то говорим NAS'у перечитать инфу об абоненте if abon.ip_address != ip_address: - tc = get_TransmitterClientKlass()() tc.signal_abon_refresh_info(abon) #return redirect('abonhome_link', gid, uid) @@ -326,7 +331,7 @@ def buy_tariff(request, gid, uid): cd = frm.cleaned_data abon.buy_tariff(cd['tariff'], request.user) abon.save() - return redirect('abonhome_link', uid=abon.id) + return redirect('abonhome_link', gid=gid, uid=abon.id) else: warntext = u'Что-то не так при покупке услуги, проверьте и попробуйте ещё' else: @@ -363,25 +368,37 @@ def chpriority(request, gid, uid): @login_required def complete_service(request, gid, uid, srvid): abtar = get_object_or_404(models.AbonTariff, id=srvid) + abon_group = get_object_or_404(models.AbonGroup, id=gid) if abtar.abon.id != int(uid): return HttpResponse('

uid not equal uid from service

') try: if request.method == 'POST': + tc = get_TransmitterClientKlass()() + abon = abtar.abon # досрочно завершаем услугу try: + # пробуем активировать следующую услугу abtar.finish_and_activate_next_tariff(request.user) - # завершаем текущую услугу. - abtar.delete() except models.LogicError: - # Значит у абонента нет следующих услуг. Сигналим о закрытии инета в NAS - tc = get_TransmitterClientKlass()() - tc.signal_abon_close_inet(abtar.abon) + # Значит у абонента нет следующих услуг. Игнорим, далее в tariff.manage_access() всё разрулится + pass + + # завершаем текущую услугу. + abtar.delete() # Переупорядочиваем приоритеты - models.AbonTariff.objects.update_priorities(abtar.abon) + models.AbonTariff.objects.update_priorities(abon) + + # проверяем, может-ли абонент пользоваться новым тарифным планом + if abon.is_access(): + # обновляем инфу об абоненте, чтоб применился новый тариф + tc.signal_abon_refresh_info(abon) + else: + # если доступа нет - закрываем инет + tc.signal_abon_close_inet(abon) return redirect('abonhome_link', gid, uid) @@ -401,12 +418,11 @@ def complete_service(request, gid, uid, srvid): 'minutes': time_use.seconds / 60 % 60 } return render(request, 'abonapp/complete_service.html', { - 'csrf_token': csrf(request)['csrf_token'], 'abtar': abtar, - 'abon': get_object_or_404(models.Abon, id=uid), + 'abon': abtar.abon, 'next_tariff': next_tariff[0] if next_tariff.count() > 0 else None, 'time_use': time_use, - 'abon_group': get_object_or_404(models.AbonGroup, id=gid) + 'abon_group': abon_group }) except models.LogicError as e: @@ -416,10 +432,10 @@ def complete_service(request, gid, uid, srvid): warntext = e.value return render(request, 'abonapp/complete_service.html', { - 'csrf_token': csrf(request)['csrf_token'], 'abtar': abtar, - 'uid': uid, - 'warntext': warntext + 'abon': abtar.abon, + 'warntext': warntext, + 'abon_group': abon_group }) diff --git a/agent.py b/agent.py old mode 100644 new mode 100755 index 82803fd..c57028c --- a/agent.py +++ b/agent.py @@ -1,4 +1,4 @@ -#!/bin/env python +#!/bin/env python2 import os from agent import main diff --git a/agent/db.py b/agent/db.py index 7416115..229f2ce 100644 --- a/agent/db.py +++ b/agent/db.py @@ -1,19 +1,20 @@ # -*- coding:utf-8 -*- import requests from json import loads +from requests.exceptions import ConnectionError from models import deserialize_tariffs, deserialize_abonents import settings def load_from_db(): - r = requests.get('%s://%s:%d/abons/api/abons' % ( - 'https' if settings.IS_USE_SSL else 'http', - settings.SERVER_IP, - settings.SERVER_PORT - ), verify=False) try: + r = requests.get('%s://%s:%d/abons/api/abons' % ( + 'https' if settings.IS_USE_SSL else 'http', + settings.SERVER_IP, + settings.SERVER_PORT + ), verify=False) user_data = loads(r.text) - del r + # Получаем тарифы tariffs = deserialize_tariffs(user_data) @@ -23,5 +24,8 @@ def load_from_db(): return abons, tariffs except ValueError as e: - print 'Error:', e, r.text - return + print('Error:', e, r.text) + + except ConnectionError: + print("Can not connect to server %s:%d..." % (settings.SERVER_IP, settings.SERVER_PORT)) + exit(0) diff --git a/agent/firewall.py b/agent/firewall.py index c9b2379..a582f1a 100644 --- a/agent/firewall.py +++ b/agent/firewall.py @@ -1,4 +1,5 @@ # -*- coding:utf-8 -*- +from agent.models import Abonent, Tariff class FirewallManager(object): @@ -16,6 +17,7 @@ class FirewallManager(object): # Открывает доступ в интернет def open_inet_door(self, user): + assert isinstance(user, Abonent) if not user.tariff: print u'WARNING: User does not have a tariff' return @@ -27,6 +29,7 @@ class FirewallManager(object): # Закрывает доступ в интернет def close_inet_door(self, user): + assert isinstance(user, Abonent) cmd = r"%s table 12 del %s/32 && %s table 13 del %s/32" % ( self.f, user.ip_str(), self.f, user.ip_str() @@ -35,11 +38,13 @@ class FirewallManager(object): # Создаёт тариф (пайпы, режущие скорость def make_tariff(self, tariff): + assert isinstance(tariff, Tariff) cmd = r"make ipfw tariff :)" self.exec_cmd(cmd) # Убирает тариф из фаервола def destroy_tariff(self, tariff): + assert isinstance(tariff, Tariff) cmd = r"destroy ipfw tariff :)" self.exec_cmd(cmd) diff --git a/agent/main.py b/agent/main.py index 5d049ea..9c567b2 100644 --- a/agent/main.py +++ b/agent/main.py @@ -4,12 +4,14 @@ from db import load_from_db from firewall import FirewallManager from time import sleep from sslTransmitter import TransmitServer +from agent.models import Abonent, Tariff def filter_user_by_id(users, uid): - usrs = filter(lambda usr: usr.uid == uid, users) - if len(usrs) > 0: - return usrs[0] + users = filter(lambda usr: isinstance(usr, Abonent), users) + users = filter(lambda usr: usr.uid == uid, users) + if len(users) > 0: + return users[0] else: return @@ -21,7 +23,7 @@ def main(debug=False): # Инициализация абонентов if debug: - print u'Инициализация...' + print("Инициализация...") # Открываем доступ в инет тем кто активен и у кого подключён тариф for usr in filter(lambda usr: usr.is_active, users): @@ -30,13 +32,14 @@ def main(debug=False): if usr.tariff: # Открываем доступ в инет frw.open_inet_door(usr) + if debug: print "Разрешён доступ в инет для:", usr.ip_str() # Слушем в отдельном процессе сеть на предмет событий ts = TransmitServer('127.0.0.1', 2134) ts.start() if debug: - print u"Загружено %d абонентов" % len(users) + print("Загружено %d абонентов" % len(users)) while True: @@ -44,9 +47,7 @@ def main(debug=False): events = ts.get_data() # Проходим по появившимся событиям for event in events: - #event.toa - #event.id - #event.dt + #event.toa, event.id, event.dt # Смотрим тип события toa = int(event.toa) @@ -108,7 +109,7 @@ def main(debug=False): frw.destroy_tariff(tariff) frw.make_tariff(tariff) else: - print 'WARNING: не найден тариф для которого сигнал на изменение данных, пробуем перезагрузиться' + print('WARNING: не найден тариф для которого возбуждён сигнал на изменение данных, пробуем перезагрузиться') return # Очищаем очередь событий diff --git a/agent/models.py b/agent/models.py index 6b8d675..9779160 100644 --- a/agent/models.py +++ b/agent/models.py @@ -81,12 +81,14 @@ class Abonent(Serializer): is_active = True def __init__(self, uid=None, ip=None, tariff=None): + # none потому что может инициализироваться пустым, чтоб быть распакованным через deserialize() + if tariff: + assert isinstance(tariff, Tariff) self.ip = ip self.uid = uid self.tariff = tariff def ip_str(self): - # int2ip, Example out '127.0.0.1' return socket.inet_ntoa(struct.pack("!I", self.ip)) def _serializable_obj(self): @@ -98,6 +100,10 @@ class Abonent(Serializer): } def deserialize(self, dump, tariffs): + # фильтруем только элементы нужного типа + tariffs = filter(lambda trf: isinstance(trf, Tariff), tariffs) + assert len(tariffs) > 0 + inf = loads(dump) if type(dump) == str else dump self.uid = int(inf['id']) self.is_active = bool(inf['is_active']) diff --git a/agent/sslTransmitter.py b/agent/sslTransmitter.py index f25c029..7f1ffa4 100644 --- a/agent/sslTransmitter.py +++ b/agent/sslTransmitter.py @@ -67,10 +67,12 @@ def agent_abon_typer(fn): if isinstance(abon, Abonent): fn(self, abon) else: + act_tar = abon.active_tariff() + agent_tariff = Tariff(act_tar.id, act_tar.speedIn, act_tar.speedOut) if act_tar else None abn = Abonent( abon.id, abon.ip_address.int_ip() if abon.ip_address else 0, - abon.active_tariff() + agent_tariff ) fn(self, abn) return wrapped @@ -108,7 +110,10 @@ class SSLTransmitterClient(object): port or settings.SELF_PORT )) except socket.error: - raise NetExcept(u'Ошибка подключения к NAS агенту') + raise NetExcept('Ошибка подключения к NAS агенту %s:%d' % ( + ip or settings.SELF_IP, + port or settings.SELF_PORT + )) def write(self, d): self.s.write(d) @@ -162,7 +167,8 @@ class SSLTransmitterClient(object): ) def __del__(self): - self.s.close() + if self.s: + self.s.close() class PlainTransmitterClient(SSLTransmitterClient): @@ -176,12 +182,16 @@ class PlainTransmitterClient(SSLTransmitterClient): )) self.s = s except socket.error: - raise NetExcept(u'Ошибка подключения к NAS агенту') + raise NetExcept('Ошибка подключения к NAS агенту на %s:%d' % ( + ip or settings.SELF_IP, + port or settings.SELF_PORT + )) def write(self, d): self.s.send(d) +# общалка с NAS'ом def get_TransmitterClientKlass(): if settings.IS_USE_SSL: return SSLTransmitterClient diff --git a/agent/tariff.py b/agent/tariff.py deleted file mode 100644 index 380474e..0000000 --- a/agent/tariff.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding:utf-8 -*- diff --git a/devapp/models.py b/devapp/models.py index 033a893..67835a5 100644 --- a/devapp/models.py +++ b/devapp/models.py @@ -18,6 +18,9 @@ class Device(models.Model): man_passw = models.CharField(max_length=16, null=True, blank=True) #map_dot = models.ForeignKey() + class Meta: + db_table = 'dev' + def get_abons(self): pass @@ -44,6 +47,7 @@ class Port(models.Model): speed = models.CharField(max_length=1, default=PORT_SPEEDS[0][0], choices=PORT_SPEEDS) class Meta: + db_table = 'dev_port' unique_together = (('device', 'num')) diff --git a/djing/settings.py b/djing/settings.py index 1e841d5..a9af4e3 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -35,7 +35,8 @@ INSTALLED_APPS = [ 'searchapp', 'devapp', 'mapapp', - 'statistics' + 'statistics', + 'taskapp' ] MIDDLEWARE_CLASSES = [ diff --git a/djing/settings_example.py b/djing/settings_example.py index 3745bb3..75e0c16 100644 --- a/djing/settings_example.py +++ b/djing/settings_example.py @@ -35,7 +35,8 @@ INSTALLED_APPS = [ 'searchapp', 'devapp', 'mapapp', - 'statistics' + 'statistics', + 'taskapp' ] MIDDLEWARE_CLASSES = [ diff --git a/djing/urls.py b/djing/urls.py index a47314a..50ef853 100644 --- a/djing/urls.py +++ b/djing/urls.py @@ -14,6 +14,7 @@ urlpatterns = [ url(r'^dev/', include('devapp.urls')), url(r'^map/', include('mapapp.urls')), url(r'^statistic/', include('statistics.urls')), + url(r'^tasks/', include('taskapp.urls')), url(r'^admin/', admin.site.urls), ] diff --git a/tariff_app/base_intr.py b/tariff_app/base_intr.py index 997223d..e6f3c63 100644 --- a/tariff_app/base_intr.py +++ b/tariff_app/base_intr.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from abc import ABCMeta, abstractmethod +#from abonapp import Abon -class TariffBase(object): +class TariffBase: __metaclass__ = ABCMeta # Принимает на вход: @@ -14,3 +15,15 @@ class TariffBase(object): @staticmethod def description(): """Возвращает текстовое описание""" + + @staticmethod + def manage_access(abon): + """Управляет доступом абонента к услуге""" + #assert isinstance(abon, Abon) + # если абонент не активен то выходим + if not abon.is_active: return False + # смотрим на текущую услугу + act_tar = abon.active_tariff() + # если есть услуга + if act_tar: + return True diff --git a/tariff_app/custom_tariffs.py b/tariff_app/custom_tariffs.py index 7aaa8c7..3993156 100644 --- a/tariff_app/custom_tariffs.py +++ b/tariff_app/custom_tariffs.py @@ -2,12 +2,14 @@ from django.utils import timezone from datetime import datetime, timedelta from base_intr import TariffBase +#from abonapp import AbonTariff class TariffDefault(TariffBase): # Базовый функционал считает стоимость пропорционально использованному времени def calc_amount(self, abon_tariff): + #assert isinstance(abon_tariff, AbonTariff) # сейчас nw = datetime.now(tz=timezone.get_current_timezone()) diff --git a/taskapp/apps.py b/taskapp/apps.py index 205f692..c51c95a 100644 --- a/taskapp/apps.py +++ b/taskapp/apps.py @@ -1,5 +1,5 @@ from django.apps import AppConfig -class TicketsappConfig(AppConfig): +class TaskappConfig(AppConfig): name = 'taskapp' diff --git a/templates/abonapp/peoples.html b/templates/abonapp/peoples.html index 282d0cc..ee11c39 100644 --- a/templates/abonapp/peoples.html +++ b/templates/abonapp/peoples.html @@ -46,7 +46,9 @@ {% for human in peoples %} - + {% if human.is_active %} + {% else %} + {% endif %} {{ human.id }} {{ human.username }} {{ human.ip_address|default:'Не назначен' }} diff --git a/templates/base.html b/templates/base.html index 4e02775..57b4c56 100644 --- a/templates/base.html +++ b/templates/base.html @@ -21,10 +21,10 @@