From 59e92f4ff49d4b14dc7da45b63053e617929f06a Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Sat, 20 Oct 2018 15:48:40 +0300 Subject: [PATCH] revert to single subnet per subscriber --- abonapp/models.py | 1 - abonapp/templates/abonapp/peoples.html | 10 +- ...015_1430.py => 0003_auto_20181019_1230.py} | 27 ++-- ip_pool/models.py | 2 - nas_app/nas_managers/mod_mikrotik.py | 141 +++++------------- nas_app/nas_managers/structs.py | 24 +-- 6 files changed, 64 insertions(+), 141 deletions(-) rename ip_pool/migrations/{0003_auto_20181015_1430.py => 0003_auto_20181019_1230.py} (57%) diff --git a/abonapp/models.py b/abonapp/models.py index b3f81b7..59c692b 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -238,7 +238,6 @@ class Abon(BaseAccount): name="uid%d" % self.pk, network=self.ip_address, max_limit=(abon_tariff.speedIn, abon_tariff.speedOut), - queue_type=SubnetQueue.QUEUE_LEAF, is_access=self.is_access() ) diff --git a/abonapp/templates/abonapp/peoples.html b/abonapp/templates/abonapp/peoples.html index de9e206..843fc3a 100644 --- a/abonapp/templates/abonapp/peoples.html +++ b/abonapp/templates/abonapp/peoples.html @@ -32,6 +32,7 @@ {% if order_by == 'username' %}{% endif %} {% trans 'Last traffic' %} + {% trans 'Network' %} {% trans 'fio' %} @@ -88,9 +89,10 @@ {% endif %} {% endif %} - {{ human.fio }} - {{ human.street|default:_('Not assigned') }} - {{ human.house|default:'-' }} + {{ human.ip_address|default_if_none:'—' }} + {{ human.fio|default:'—' }} + {{ human.street|default:_('Not assigned') }} + {{ human.house|default:'—' }} {{ human.telephone }} {% if human.current_tariff %} @@ -117,7 +119,7 @@ {% empty %} - + {% trans 'Subscribers not found' %}. {% if perms.abonapp.add_abon %} {% trans 'Add abon' %} diff --git a/ip_pool/migrations/0003_auto_20181015_1430.py b/ip_pool/migrations/0003_auto_20181019_1230.py similarity index 57% rename from ip_pool/migrations/0003_auto_20181015_1430.py rename to ip_pool/migrations/0003_auto_20181019_1230.py index 7870879..1f25d68 100644 --- a/ip_pool/migrations/0003_auto_20181015_1430.py +++ b/ip_pool/migrations/0003_auto_20181019_1230.py @@ -1,4 +1,4 @@ -# Generated by Django 2.1 on 2018-10-15 14:30 +# Generated by Django 2.1 on 2018-10-19 12:30 from django.db import migrations, models import djing.fields @@ -14,10 +14,23 @@ class Migration(migrations.Migration): migrations.CreateModel( name='LeasesHistory', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID' + )), ('ip', models.GenericIPAddressField(verbose_name='Ip address')), - ('lease_time', models.DateTimeField(auto_now_add=True, verbose_name='Lease time')), - ('mac_addr', djing.fields.MACAddressField(blank=True, integer=True, null=True, verbose_name='Mac address')), + ('lease_time', models.DateTimeField( + auto_now_add=True, + verbose_name='Lease time' + )), + ('mac_addr', djing.fields.MACAddressField( + blank=True, + integer=True, + null=True, + verbose_name='Mac address' + )), ], options={ 'verbose_name': 'History lease', @@ -34,10 +47,4 @@ class Migration(migrations.Migration): model_name='ipleasemodel', name='is_dynamic', ), - migrations.AddField( - model_name='networkmodel', - name='speed', - field=models.FloatField(default=0.0, verbose_name='Speed for subnet'), - preserve_default=False, - ), ] diff --git a/ip_pool/models.py b/ip_pool/models.py index 4ac7c2a..daf15bd 100644 --- a/ip_pool/models.py +++ b/ip_pool/models.py @@ -39,8 +39,6 @@ class NetworkModel(models.Model): ip_start = models.GenericIPAddressField(_('Start work ip range')) ip_end = models.GenericIPAddressField(_('End work ip range')) - speed = models.FloatField(_('Speed for subnet')) - def __str__(self): netw = self.get_network() return "%s: %s" % (self.description, netw.with_prefixlen) diff --git a/nas_app/nas_managers/mod_mikrotik.py b/nas_app/nas_managers/mod_mikrotik.py index 84dfa58..571eb16 100644 --- a/nas_app/nas_managers/mod_mikrotik.py +++ b/nas_app/nas_managers/mod_mikrotik.py @@ -4,14 +4,13 @@ import socket from abc import ABCMeta from hashlib import md5 from ipaddress import ip_network, _BaseNetwork -from typing import Iterable, Optional, Tuple, Generator, Dict, Iterator, Any +from typing import Iterable, Optional, Tuple, Generator, Dict, Iterator from django.conf import settings from django.utils.translation import ugettext_lazy as _ from djing.lib.decorators import LazyInitMetaclass from nas_app.nas_managers import core from nas_app.nas_managers import structs as i_structs -from ip_pool.models import NetworkModel DEBUG = getattr(settings, 'DEBUG', False) @@ -21,14 +20,14 @@ LIST_DEVICES_ALLOWED = 'DjingDevicesAllowed' class ApiRos(object): """Routeros api""" - sk = None + __sk = None is_login = False def __init__(self, ip: str, port: int): - if self.sk is None: + if self.__sk is None: sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.connect((ip, port or 8728)) - self.sk = sk + self.__sk = sk def login(self, username, pwd): if self.is_login: @@ -147,7 +146,7 @@ class ApiRos(object): def write_bytes(self, s): n = 0 while n < len(s): - r = self.sk.send(s[n:]) + r = self.__sk.send(s[n:]) if r == 0: raise core.NasFailedResult("connection closed by remote end") n += r @@ -155,16 +154,15 @@ class ApiRos(object): def read_bytes(self, length): ret = b'' while len(ret) < length: - s = self.sk.recv(length - len(ret)) + s = self.__sk.recv(length - len(ret)) if len(s) == 0: raise core.NasFailedResult("connection closed by remote end") ret += s return ret def __del__(self): - sk = getattr(self, 'sk') - if sk is not None: - self.sk.close() + if self.__sk is not None: + self.__sk.close() class MikrotikTransmitter(core.BaseTransmitter, ApiRos, @@ -247,14 +245,6 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, is_access=not disabled, queue_id=info.get('=.id') ) - if name.startswith('uid'): - a.queue_type = i_structs.SubnetQueue.QUEUE_LEAF - elif name.startswith('net_'): - a.queue_type = i_structs.SubnetQueue.QUEUE_SUBNET - elif name == 'queue-root': - a.queue_type = i_structs.SubnetQueue.QUEUE_ROOT - else: - a.queue_type = i_structs.SubnetQueue.QUEUE_UNKNOWN return a except ValueError as e: print('ValueError:', e) @@ -269,8 +259,7 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, if r: return self._build_shape_obj(r.get('!re')) - def add_queue(self, queue: i_structs.SubnetQueue, - parent_name: str) -> None: + def add_queue(self, queue: i_structs.SubnetQueue) -> None: if not isinstance(queue, i_structs.SubnetQueue): raise TypeError('queue must be instance of SubnetQueue') self._exec_cmd(( @@ -279,10 +268,9 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, # FIXME: тут в разных микротиках или =target-addresses или =target '=target=%s' % queue.network, '=max-limit=%.3fM/%.3fM' % queue.max_limit, - '=queue=Djing_SFQ/Djing_SFQ', + '=queue=Djing_pcq/Djing_pcq', '=burst-time=1/1', - '=parent=%s' % parent_name, - '=total-queue=Djing_SFQ' + '=total-queue=Djing_pcq' )) def remove_queue(self, queue: i_structs.SubnetQueue) -> None: @@ -302,13 +290,13 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, if len(ids) > 1: self._exec_cmd(('/queue/simple/remove', '=numbers=%s' % ids)) - def update_queue(self, queue: i_structs.SubnetQueue, parent_name: str): + def update_queue(self, queue: i_structs.SubnetQueue): if not isinstance(queue, i_structs.SubnetQueue): raise TypeError if not queue.queue_id: queue = self.find_queue(queue.name) if queue is None: - return self.add_queue(queue, parent_name) + return self.add_queue(queue) else: cmd = [ '/queue/simple/set', @@ -317,8 +305,7 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, # FIXME: тут в разных версиях прошивки микротика # или =target-addresses или =target '=target=%s' % queue.network, - '=queue=Djing_SFQ/Djing_SFQ', - '=parent=%s' % parent_name, + '=queue=Djing_pcq/Djing_pcq', '=burst-time=1/1' ] if queue.queue_id: @@ -375,8 +362,9 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, '?dynamic=no' )) for dat in nets: - yield ip_network(dat.get('=address'), strict=False), dat.get( - '=.id') + n = ip_network(dat.get('=address')) + n.queue_id = dat.get('=.id') + yield n ################################################# # BaseTransmitter implementation @@ -397,9 +385,9 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, if ip_list_entity: self.remove_ip(ip_list_entity.get('=.id')) - def add_user(self, queue: i_structs.SubnetQueue, parent_name=None, *args): + def add_user(self, queue: i_structs.SubnetQueue, *args): try: - self.add_queue(queue, parent_name=parent_name) + self.add_queue(queue) except core.NasFailedResult as e: print('Error:', e) net = queue.network @@ -416,9 +404,8 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, ip_id = r.get('=.id') self.remove_ip(ip_id) - def update_user(self, queue: i_structs.SubnetQueue, parent_name=None, - *args): - self.update_queue(queue, parent_name) + def update_user(self, queue: i_structs.SubnetQueue, *args): + self.update_queue(queue) def ping(self, host, count=10) -> Optional[Tuple[int, int]]: r = self._exec_cmd(( @@ -441,80 +428,30 @@ class MikrotikTransmitter(core.BaseTransmitter, ApiRos, def read_users(self) -> i_structs.VectorQueue: return self.read_queue_iter() - @staticmethod - def _build_db_queues(users_from_db: Iterator[Any]) -> Generator: - # Корневая очередь - # FIXME: Корневую очередь надо брать откуда-то - root_queue = i_structs.SubnetQueue( - name='queue-root', - network='10.0.0.0/8', - max_limit=2048, - queue_type=i_structs.SubnetQueue.QUEUE_ROOT - ) - - # выберем структуры подсетей - db_subnet_queues = (i_structs.SubnetQueue( - name="net_%s" % db_net.network, - network=db_net.get_network(), - max_limit=float(db_net.speed), - queue_type=i_structs.SubnetQueue.QUEUE_SUBNET - ) for db_net in NetworkModel.objects.all().iterator()) - - queues_struct_gen = ( + def sync_nas(self, users_from_db: Iterator): + queues_from_db = ( ab.build_agent_struct() for ab in users_from_db if ab is not None and ab.is_access() ) + queues_from_db = set(filter(lambda x: x is not None, queues_from_db)) + queues_from_gw = self.read_queue_iter() - r = [q for q in queues_struct_gen if q is not None] - r.insert(0, root_queue) - r.extend(db_subnet_queues) - return r, root_queue - - def _queues_diff(self, users_from_db: Iterator): - queues_from_db, root_queue = self._build_db_queues(users_from_db) - queues_from_gw = tuple(self.read_queue_iter()) - - # TODO: надо чтоб корневая очередь тоже создавалась - - db_queues_subnets = ( - q for q in queues_from_db - if q.queue_type == i_structs.SubnetQueue.QUEUE_SUBNET - ) - gw_queues_subnets = tuple( - q for q in queues_from_gw - if q.queue_type == i_structs.SubnetQueue.QUEUE_SUBNET - ) - subnets_for_add, subnets_for_del = core.diff_set( - set(db_queues_subnets), set(gw_queues_subnets)) - - self.remove_queue_range( - (q.queue_id for q in subnets_for_del) - ) - for q in subnets_for_add: - self.add_queue(q, parent_name=root_queue.name) - del subnets_for_add, subnets_for_del - - db_queue_users = ( - q for q in queues_from_db - if q.queue_type == i_structs.SubnetQueue.QUEUE_LEAF - ) - gw_queue_users = ( - q for q in queues_from_gw - if q.queue_type == i_structs.SubnetQueue.QUEUE_LEAF - ) - user_q_for_add, user_q_for_del = core.diff_set(set(db_queue_users), - set(gw_queue_users)) + user_q_for_add, user_q_for_del = core.diff_set(queues_from_db, + set(queues_from_gw)) self.remove_queue_range( (q.queue_id for q in user_q_for_del) ) for q in user_q_for_add: - find_filter = filter( - lambda qe: qe.network.overlaps(q.network), - gw_queues_subnets - ) - parent_subnet = next(find_filter, root_queue) - self.add_queue(q, parent_name=parent_subnet.name) - - def sync_nas(self, users_from_db: Iterator): - self._queues_diff(users_from_db) + self.add_queue(q) + del user_q_for_add, user_q_for_del + + # sync ip addrs list + db_nets = set(net.network for net in queues_from_db) + gw_nets = set(self.read_nets_iter(LIST_USERS_ALLOWED)) + nets_add, nets_del = core.diff_set(db_nets, gw_nets) + self.remove_ip_range( + (q.queue_id for q in nets_del) + ) + for q in nets_add: + self.add_ip(LIST_USERS_ALLOWED, q) diff --git a/nas_app/nas_managers/structs.py b/nas_app/nas_managers/structs.py index 7fa105f..7cd8230 100644 --- a/nas_app/nas_managers/structs.py +++ b/nas_app/nas_managers/structs.py @@ -8,22 +8,14 @@ class BaseStruct(object, metaclass=ABCMeta): class SubnetQueue(BaseStruct): - __slots__ = ('name', '_net', '_max_limit', '_queue_type', - 'is_access', 'queue_id') - - # Queue types - QUEUE_UNKNOWN = 0 - QUEUE_ROOT = 1 - QUEUE_SUBNET = 2 - QUEUE_LEAF = 3 + __slots__ = ('name', '_net', '_max_limit', 'is_access', 'queue_id') def __init__(self, name: str, network, max_limit=0.0, - queue_type=QUEUE_UNKNOWN, is_access=True, queue_id=None): + is_access=True, queue_id=None): super().__init__() self.name = name self.network = network self.max_limit = max_limit - self.queue_type = queue_type self.is_access = is_access self.queue_id = queue_id @@ -57,18 +49,6 @@ class SubnetQueue(BaseStruct): network = property(get_network, set_network) - def get_queue_type(self): - return self._queue_type - - def set_queue_type(self, v): - if not isinstance(v, int): - raise ValueError('queue_type must be int') - if v < self.QUEUE_UNKNOWN or v > self.QUEUE_LEAF: - raise IndexError('queue_type out of range') - self._queue_type = v - - queue_type = property(get_queue_type, set_queue_type) - def __eq__(self, other): return self.network == other.network and self.max_limit == other.max_limit