Browse Source

revert to single subnet per subscriber

devel
Dmitry Novikov 7 years ago
parent
commit
59e92f4ff4
  1. 1
      abonapp/models.py
  2. 10
      abonapp/templates/abonapp/peoples.html
  3. 27
      ip_pool/migrations/0003_auto_20181019_1230.py
  4. 2
      ip_pool/models.py
  5. 141
      nas_app/nas_managers/mod_mikrotik.py
  6. 24
      nas_app/nas_managers/structs.py

1
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()
)

10
abonapp/templates/abonapp/peoples.html

@ -32,6 +32,7 @@
{% if order_by == 'username' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="hidden-xs">{% trans 'Last traffic' %}</th>
<th>{% trans 'Network' %}</th>
<th class="col-xs-3">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_order_by request order_by='fio' %}">
{% trans 'fio' %}
@ -88,9 +89,10 @@
{% endif %}
{% endif %}
</td>
<td class="col-xs-2">{{ human.fio }}</td>
<td class="col-xs-2">{{ human.street|default:_('Not assigned') }}</td>
<td class="col-xs-1">{{ human.house|default:'-' }}</td>
<td class="col-xs-1">{{ human.ip_address|default_if_none:'&mdash;' }}</td>
<td class="col-xs-2">{{ human.fio|default:'&mdash;' }}</td>
<td class="col-xs-1">{{ human.street|default:_('Not assigned') }}</td>
<td class="col-xs-1">{{ human.house|default:'&mdash;' }}</td>
<td class="col-xs-1"><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></td>
<td class="col-xs-2">
{% if human.current_tariff %}
@ -117,7 +119,7 @@
</tr>
{% empty %}
<tr>
<td colspan="11">
<td colspan="12">
{% trans 'Subscribers not found' %}.
{% if perms.abonapp.add_abon %}
<a href="{% url 'abonapp:add_abon' group.pk %}">{% trans 'Add abon' %}</a>

27
ip_pool/migrations/0003_auto_20181015_1430.py → 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,
),
]

2
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)

141
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)

24
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

Loading…
Cancel
Save